Skip to end of metadata
Go to start of metadata

Almost every web application has more than one page. And more often that not, you want to apply a consistent theme or layout across all those pages (or perhaps distinct layouts for each subset of pages). In the very beginning a common approach was to write JSPs like this:

Rudimentary layout reuse by include
<jsp:include page="/nav/header.jsp"/>
...
<jsp:include page="/nav/context.jsp"/>
My page content here
<jsp:include page="/nav/footer.jsp" />

This worked reasonably well for simple cases, but runs into problems. It's also easy to screw up the formatting by not including the JSPs at exactly the right point. Or maybe, because you wanted to insert scripts or styles into the HTML <head> you ended up having each JSP define the <head> and open and close the <body> etc. The more you do the easier it is to become inconsistent. Not to mention that you have to change every page in the site if you want to change your layout (C-Clamp looking a little dated?). And that it's now virtually impossible to have pages render in different layouts without modifying the page.

I'm not going to pretend this problem hasn't been solved. It has. Tools exist like Tiles and Sitemesh. But I don't think it's been solved in a way that is ridiculously easy to use! Tiles, while very full featured, is monstrously complex for how 99% of people use it. I like Sitemesh a lot, and it provides some things that Stripes' layout tags (which we'll get to in a second) don't. But it requires some XML config, and it requires another library etc. etc. So Stripes provides a very simple solution to the problem, that is quite powerful. If it meets your needs I'd recommend you use it. If not, take a look at Sitemesh.

Basic layouts with the Stripes Layout Tags

Stripes includes three simple tags for creating and using layouts. They are:

Stripes uses JSPs to both define and render the layouts. The following is an example of a really simple layout, with a consistent header and footer, using the Stripes layout tags:

/layout/default.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="stripes" uri="http://stripes.sourceforge.net/stripes.tld" %>
 
<stripes:layout-definition>
<html>
    <head>
        <title>Layout Example</title>
        <link rel="stylesheet"
            type="text/css"
            xhref="${pageContext.request.contextPath}/style/default.css"/>
        <stripes:layout-component name="html_head"/>
    </head>
 
    <body>
        <stripes:layout-component name="header">
            <jsp:include page="/layout/_header.jsp"/>
        </stripes:layout-component>
 
        <div class="pageContent">
            <stripes:layout-component name="contents"/>
        </div>
 
        <stripes:layout-component name="footer">
            <jsp:include page="/layout/_footer.jsp"/>
        </stripes:layout-component>
    </body>
</html>
</stripes:layout-definition>

When this layout is rendered the following things happen:

  • Content outside of the <stripes:layout-definition> is blithely ignored
  • Any content inside the <stripes:layout-definition> but outside of a <stripes:layout-component> is always rendered
  • When a <stripes:layout-component> is encountered its body is rendered unless it has been overridden, in which case the overridden value is rendered instead

Let's take a look at a Hello World page that uses this layout.

/HelloWorld.jsp
<%@ taglib prefix="stripes" uri="http://stripes.sourceforge.net/stripes.tld" %>
<stripes:layout-render name="/layout/default.jsp">
    <stripes:layout-component name="contents">
        Hello World!
    </stripes:layout-component>
</stripes:layout-render>

Notice how the "name" of the layout is simply the path to the JSP that defines the layout. In this example we use a <stripes:layout-component> tag again, but this time to override the value of the "pageContent" component of the layout. Any number of components in the layout can be overridden. In the example layout above there is a component called "html_head". A page can override that component, not to replace the html head in the layout, but to contribute to it (perhaps to add a <script> block or some meta tags).

Use absolute path names

Always use absolute path names, starting with a forward slash /, in the name= attribute of the stripes:layout-render tag.

Use valid Java identifiers as component names

Although using a component name such as "body-content" (with a hyphen in the name) might work in some situations, it can cause problems that are hard to track down. Please consider using valid Java identifiers as component names as a best practice. In particular, use underscores instead of hyphens, as in "body_content", do not use spaces in the name, and so on.

Passing additional information to the layout

Already this is pretty useful, but say you want your pages to look even more standard. Perhaps the page title should always be displayed in a particular style, and always in the same place. Perhaps you want to make the window title include the page title too. Well, you could force your users to provide <stripes:layout-components> for each little thing like the page title, but that starts to feel clunky.

For just this reason the <stripes:layout-render> tag accepts dynamic attributes. This means you can supply any value you like as an attribute to the <stripes:layout-render> tag. All such attributes are made available to the layout definition as page context attributes. And since you can refer to page context attributes using EL, and all sorts of tags, that's pretty handy. Imagine we modified our JSP example above to:

/HelloWorld.jsp
<%@ taglib prefix="stripes" uri="http://stripes.sourceforge.net/stripes.tld" %>
<stripes:layout-render name="/layout/default.jsp" pageTitle="Using A Layout">
    <stripes:layout-component name="contents">
        Hello World!
    </stripes:layout-component>
</stripes:layout-render>

we could then use the pageTitle in our layout definition:

/layout/default.jsp
...
<stripes:layout-definition>
<html>
    <head>
        <title>Examples: ${pageTitle}</title>
        <link rel="stylesheet"
            type="text/css"
            xhref="${pageContext.request.contextPath}/style/default.css"/>
        <stripes:layout-component name="html_head"/>
    </head>
 
    <body>
        <stripes:layout-component name="header">
            <jsp:include page="/layout/_header.jsp"/>
        </stripes:layout-component>
 
        <div class="title>${pageTitle}</div>
 
        <div class="pageContent">
            <stripes:layout-component name="contents"/>
        </div>
 
        <stripes:layout-component name="footer">
            <jsp:include page="/layout/_footer.jsp"/>
        </stripes:layout-component>
    </body>
</html>
</stripes:layout-definition>

But suppose in some cases the page title isn't quite so simple? Perhaps we want to include the user's name and do some formatting or internationalization? Well, it just so happens that layout-components are also made available in the page context under their name. So we could happily define another JSP that passes the pageTitle this way:

/HelloWorld2.jsp
<%@ taglib prefix="stripes" uri="http://stripes.sourceforge.net/stripes.tld" %>
<stripes:layout-render name="/layout/default.jsp">
    <stripes:layout-component name="contents">
        Hello World!
    </stripes:layout-component>
    <stripes:layout-component name="pageTitle">
        <jsp:useBean scope="session" name="user" class="com.myco.myapp.User"/>
        <c:choose>
            <c:when test="${user.male}>Mr.</c:when>
            <c:otherwise>Ms.</c:otherwise>
        <c:choose>
        ${user.lastName} is using a Layout
    </stripes:layout-component>
</stripes:layout-render>

There are a few points worth noting here:

  • You can pass as many parameters to a layout as you like
  • The parameters can be either attributes of the render tag, or components within the render tag
  • But: only components can be pulled into the layout using the <stripes:layout-component> tag - render attributes are only available as page context attributes
  • While layout components must generate a String, render attributes can be of any type (e.g. you can pass a List or a User to the layout)

Layouts within layouts

All of the examples thus far have focused on using a single layout to lay out an entire page. In this section we'll cover using layouts for page fragments and nested or inherited layouts.

Page fragment layouts

This might be obvious, but you can also use a layout to control how a small piece of a page renders. Using the layout tags in this way is very similar to using the new JSP tag files which allow you to write custom tags using JSP fragments. With on important difference. While you can pass the result of JSP fragments to a tag file, those fragments cannot contain any scriptlets. Often this isn't a problem, but sometimes it can get in the way. Anyway, you can imagine that perhaps you want to always display images in a standard way, with a caption. You might define a layout like:

/layout/image.jsp
<stripes:layout-definition>
    <table style="minimal">
        <tr>
            <td align="center"><img xsrc="${pageContext.request.contextPath}/${src}" alt="${caption}" /></td>
        </tr>
        <tr>
            <td style="caption">${caption}</td>
        </tr>
    </table>
</stripes:layout-definition>

And then use that in your JSP to give your images a consistent look and feel:

/HelloWorld.jsp
<%@ taglib prefix="stripes" uri="http://stripes.sourceforge.net/stripes.tld" %>
<stripes:layout-render name="/layout/default.jsp" pageTitle="Using A Layout">
    <stripes:layout-component name="contents">
        <p>Lookee! An image!</p>
 
        <stripes:layout-render name="/layout/image.jsp"
            xsrc="/HelloWorld.jpg"
            caption="Hello World!"/>
    </stripes:layout-component>
</stripes:layout-render>

Now when your client suddenly decides they want captions on the side instead of below images, you can just go change the layout, and not have to update every JSP page in the entire site. Excellent.

Nested layouts

Now suppose you've come this far and your site looks great, all the navigation is consistent, but you have a bunch of pages that all do searches for various kinds of things, but they all look slightly differently. Some have the search criteria above the results, some below. Some have the buttons left aligned, some right aligned. Etc. You could define a layout for searches, and re-use the global layout! Perhaps it would start out like:

/layout/search.jsp
<stripes:layout-definition>
    <stripes:layout-render name="/layout/default.jsp" pageTitle="${type} Search">
        <stripes:layout-component name="contents">
            <div class="searchFields">${inputs}</div>
            <div class="searchButtons">${buttons}</div>
            <div class="searchResults">${results}</div>
        </stripes:layout-component>
    </stripe:layout-render>
</stripes:layout-definition>

In this case we see one layout definition that uses another layout (search uses the default layout) but enforces additional structure on the pageContent component of the default layout.

Because we have a <stripes:layout-render> tag nested inside a <stripes:layout-definition> tag, any <stripes:layout-component> tags inside the render tag will be bound to the render tag - not the definition tag. That's why, in this example, we use EL expressions (inputs, buttons and results) to import the components from page context instead of <stripes:layout-component> tags.

 

  • No labels
JavaDoc - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

Complete and thorough Javadoc for Stripes can be found at https://stripesframework.ci.cloudbees.com/job/Stripes%201.5.8/javadoc/.

  • No labels
Stripes EJB3 Interceptor - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

http://code.google.com/p/stripes-ejb3/

Stripes EJB3 Interceptor is superseded by Stripes Injection Enricher

Stripes Injection Enricher satisfies injection points specified declaratively using standard Java EE annotations (@EJB, @Inject and @Resource).

Stripes already integrates with Spring to provide your ActionBean classes access to Spring resources in the form of configured Spring beans.

This extension is very similar to the Spring Interceptor, in fact it was inspired by it.

Installation and Configuration

Add Stripes EJB3 Interceptor JAR file to your classpath.

Maven Configuration
<dependency>
    <groupId>com.samaxes.stripes.ejb3</groupId>
    <artifactId>stripejb3</artifactId>
    <version>1.0.3</version>
</dependency>

Configuring Stripes for EJB3 Interceptor

Like with the Spring Interceptor you need to configure your StripesFilter. In your web.xml file locate the initialization parameters for the Stripes Filter. If you do not already have the Interceptor.Classes parameter defined, add the following:

Using the EJB Interceptor with Stripes >= 1.5
<init-param>
    <param-name>Interceptor.Classes</param-name>
    <param-value>com.samaxes.stripes.ejb3.EJBInterceptor</param-value>
</init-param>
<!-- or -->
<init-param>
    <param-name>Extension.Packages</param-name>
    <param-value>com.samaxes.stripes.ejb3</param-value>
</init-param>
Using the EJB Interceptor with Stripes <= 1.4
<init-param>
    <param-name>Interceptor.Classes</param-name>
    <param-value>
        com.samaxes.stripes.ejb3.EJBInterceptor,
        net.sourceforge.stripes.controller.BeforeAfterMethodInterceptor
    </param-value>
</init-param>

The parameter tells Stripes to use the EJB interceptor, and also not to stop using the Before/After method interceptor (which is configured by default). That's all, we're done with configurations.

Accessing EJB Beans in your ActionBean

Stripes uses the EJBInterceptor to inject EJB beans into ActionBeans when instantiated. To do this, it must be told how to inject beans and what to inject. As opposed to pushing the linkage out to an XML file, a simple annotation is used. The standard case would look something like this:

Injecting a EJB bean into an ActionBean
// Inject any properties that are annotated
@EJBBean("bugManager")
private BugManager bugManager;
 
// Inject any methods that are annotated
@EJBBean("bugManager")
public void setBugManager(BugManager bugManager) {
    this.bugManager = bugManager;
}

This extension also supports auto-wire by name. If you omit the value of the @EJBBean annotation thusly:

Auto-wiring of EJB beans in an ActionBean
// Inject any properties that are annotated
@EJBBean
private BugManager bugManager;
 
// Inject any methods that are annotated
@EJBBean
public void setBugManager(BugManager bugManager) {
    this.bugManager = bugManager;
}

Stripes will attempt to auto-wire by name. The name of the desired bean is derived from the property name or the method name - if the method name starts with 'set' then it is removed and the next character down-cased (following standard JavaBean rules), otherwise the entire method name is taken verbatim. In the example above this yields bugManager. The initial context is then queried to see if it contains a bean called bugManager.

Private and Protected Access

Stripes can inject EJB beans through both method access and field access.
If the JVM's security manager will permit it, Stripes will even inject EJB beans, when requested, into protected, package access and private fields and methods. If the JVM's security manager does not permit this, an exception will be raised explaining the problem.

Local Interface binding

If you are having trouble with JNDI binding and local interfaces use the following:

JBoss

Changing the bean JNDI binding
// your interface
public interface BugManager {
}
 
// your implementation
@Stateless
@Local(BugManager.class)
@LocalBinding(jndiBinding = "bugManager")
public class BugManagerBean implements BugManager {
}

Glassfish

Injecting a local interface into an ActionBean
// your bean implementation
@Stateless(name = "bugManager", mappedName = "bugManager")
public class BugManager implements com.Manager {
}
 
// your action bean
@EJB(name = "bugManager", beanInterface = "com.Manager")
public class BugActionBean {
    @EJBBean("java:comp/env/bugManager")
    private BugManager bugManager;
}

Apache Geronimo 2.x

It requires additional configuration of a context in the EJBInterceptor.findEJB() method.

EJBInterceptor.findEJB()
protected static Object findEJB(String name) {
    // Try to lookup using the name provided
    try {
        if (ctx == null) {
            Properties p = new Properties();
            p.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.openejb.client.LocalInitialContextFactory");
            ctx = new InitialContext(p);
        }
        Object ejb = ctx.lookup(name);
        log.debug("Found EJB bean with name [", name, "]");
        return ejb;
    } catch (NamingException e) {
        throw new StripesRuntimeException("Unable to find EJBBean with name [" + name + "] in the initial context.");
    }
}
Injecting a local interface into an ActionBean
@EJBBean("BugManagerLocal")
private BugManagerLocal bugManager;
  • No labels
Mailing Lists & Bugs - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

Your feedback is not just welcome, it is absolutely key to making Stripes even better. And not just on big issues - if you think a log message could be clearer, an exception more specific, or there's some API that isn't quite right yet, post about it to the users mailing list. If others agree, it'll get changed!

There are several ways you can find out more about Stripes and give us your feedback.

Mailing Lists

The mailing lists are maintained using the sourceforge.net mailing list system. The stripes-users list is for discussion of building applications using Stripes and where you should generally ask for help. The stripes-development is for discussion of the development of Stripes itself (i.e. not for user support). Anyone is welcome to join either or both lists, but most questions should be directed to the users list. To sign up go to the Sourceforge mailing lists page.

While the lists are archived on SourceForge the interface isn't great. The lists are also archived at GMane. GMane provides a browser based archive of the mailing list, and allows browsers to post to the list without signing up! It also provies a news (nntp) based interface with the same capabilities, and an RSS interface (which is read only).

IRC (Internet Relay Chat)

Most days you will find one or more Stripes developers along with some users at irc.freenode.net#stripes. Feel free to stop in and ask questions or just hang out and chat.

Bugs, Enhancements etc.

Stripes uses JIRA, an awesome bug tracking tool from Atlassian, the same folks who make Confluence. To search for existing bugs and enhacement requests, or to log new ones, visit:

Please take the time to search for items similar to the one you would like to submit before creating a JIRA issue. Adding specific comments to duplicate or near-duplicate bugs is far more helpful that filing new bug reports. If you are unsure whether or not something is a bug, please search the list archives and post to the users list. Thank you!

  • No labels
Localization - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

Localization is something more and more web sites are having to deal with. Localization is never easy, but Stripes does what it can to make it as painless as possible. In this document we'll cover:

Determining the Locale to use

The first challenge you're likely to face when building a localized application is to figure out what Locale to use for a given request. Chances are you will support a limited number of languages and locales. When a user submits a request, the request can contain a ordered list of locales that the user prefers. Somehow you have to decide, based upon your supported locales, and the user's preferred locales, what locale to serve them with. This is all compounded by the fact that locales can be one, two or three segments long, denoting the language, locale and variant.

Stripes uses a LocalePicker to determine the locale to use for a request. The LocalePicker is executed from the Stripes Filter, so it will execute even for direct-to-JSP navigation (read: you don't have to go through an ActionBean to take advantage of it). Once the LocalePicker has determined the locale to use, Stripes uses a HttpServletRequestWrapper to make calls to request.getLocale() and request.getLocales() to return only the chosen locale. This means that not only will Stripes use the correct locale without having to re-determine it, but that any other localization tool that relies on request.getLocale[s]() should also default to the correct locale. This includes the JSTL fmt:* tags - cool huh?

Stripes uses the DefaultLocalePicker by default. The DefaultLocalePicker uses a configured list of locales to determine the locales that the system supports. If no list is supplied it will be defaulted to a single locale equal to the system locale, i.e. the one obtained by calling java.util.Locale.getDefault(). An example list for a site that supports English (of the American variety) and Japanese might look like this:

<init-param>
    <param-name>LocalePicker.Locales</param-name>
    <param-value>en_US,ja</param-value>
</init-param>

At request time the DefaultLocalePicker runs through the user's preferred locale list, and tries to match it against the locales available in the system. The picking algorithm is such that it prefers more matching segments to less, and for the same number of matching segments, prefers locales higher up the user's preference list.

While it is clear that en_US_foo matches en_US_foo with three segments, it is also the case that en matched against en would be treated as having all three segments match. This is because in the second case, the locale and variant segments are null in both strings, and so are treated as equal.

Stripes performs locale picking on every request. This means that if a user changes their browser settings within a session, an application may serve up pages using different locales to the same user in the same session. If you want to ensure that the same locale is used for the duration of a user's session, all you need to do is extend the DefaultLocalePicker and override the pickLocale() method to place the picked locale in session, and check for it there on subsequent invocations.

Similarly if you want to employ a different algorithm to pick locales, all you need to do is implement your own LocalePicker and then configure Stripes to use it.

Determining the Character Encoding to use

Determining the correct character encoding to use can also be extremely tricky. Most browsers do not indicate a character encoding when submitting form data, and as a result the server will, by default, use the system character encoding. This can be quite problematic, especially when you handle multiple character encodings!!!

To assist in this process, Stripes allows you to designate a character encoding per Locale; simply follow the Locale with a colon and then the name of the character encoding. For example:

<init-param>
    <param-name>LocalePicker.Locales</param-name>
    <param-value>en_US:UTF-8,ja:Shift_JIS</param-value>
</init-param>

If a character encoding is specified then Stripes will ensure that all character data in the HttpServletRequest is converted to characters using the specified encoding, and that all character data sent back to the client will be encoded in the same encoding. If no character encoding is specified for locale, then Stripes does not intervene and whatever encoding the Servlet container picks will be used.

Finding localized resources

Stripes recognizes two types of localizable information that it uses. The first type is error messages. The second is field names. Each type of information is looked up using a resource bundle. If you are writing a localized application it is worth spending some time understanding resource bundles in depth - I'm not going to explain everything about them here. Suffice to say that probably the most common type of resource bundle is the PropertyResourceBundle which is simply a set of properties files with similar names.

By default Stripes uses a single resource bundle for both error messages and field names. The bundle name is StripesResources. This means that Stripes looks for properties files (in the classpath) with names like StripesResources.properties, StripesResources_en_US.properties etc. Resource bundles are semi-intelligent, so that if an exact match is not found, it will always return a bundle and will try to use the best match available.

There are two ways you can change the bundles that Stripes uses. The easiest way is to configure the DefaultLocalizationBundleFactory with different name for the bundles. You can use different bundles for error messages and field names, or use the same bundle - it's up to you.

Bundle names should always consist of the prefix (e.g. StripesResources) and never the file name (e.g. StripesResources.properties). There are multiple types of bundles, so if the properties file bundle does not meet your needs, check out the JDK documentation for other strategies.

Localized validation

There's actually not a huge deal to be said about localized validation. The built in number and date type converters use the localization capabilities of the java.text package to perform localized type conversion. So, if a user is being served pages localized into French, it is expected that the user is inputting numbers and dates appropriate to the French locale.

The BooleanTypeConverter and the EnumeratedTypeConverter do not accept localized input. Booleans are not normally displayed to the user as text or hand entered, and since the JDK doesn't provide support for localizing Booleans, neither does Stripes. If you need to do this, it shouldn't be hard to write your own(use a dictionary to lookup the words true and false in a bunch of languages etc. etc.). EnumeratedTypes by definition have a limited number of values, and these are determined at compile time, so it does not make sense to localize them.

Localized error messages

All the validation errors produced by Stripes' built in validation and type converters are instances of ScopedLocalizableError. This ensures that the error message displayed to the user can be localized if and when necessary. Take a look at the Validation Reference to see the names of the validation error messages used.

It's fairly common to use field names in validation messages, and there are a number of ways to do this. Your first option is to just hard-code the field name into the message. Since the messages themselves can be localized, that'll work, but since you might use the same error message again and again, for different fields, you'll have to copy/paste/edit that error many times.

A better strategy is to use the replacement parameters provided to insert the field name into a message template. Thus you can define a global message - {0} is a required field - instead of writing hundreds of messages with field names embedded. The {0} is replaced at runtime with the name of the field... but the field name has to come from somewhere!

If you don't define field names, Stripes will attempt to provide something vaguely user-friendly by decomposing the form field name. But this will never be localized. The better alternative is to provide localized values for your field names. This is done in the field name bundle (by default StripesResources). The syntax is:

actionPath.fieldName=Field Name 
# or just... 
fieldName=Field Name 

This allows you to be specific about field names within a form, but to also define field names that are used in many places just once. For example, if you have a form with action="/security/login.action", and a field called user.password, Stripes will first look for a resource declaration like:

/security/login.action.user.password=Secret Magic Word 

If it cannot find the above resource, it will then look for a resource declaration like:

user.password=Secret Magic Word 

At first blush the property names with slashes look a little odd, but you'll get used to it!

Localizing errors in your own code

There are (at least) two cases that may arise where you will need to manufacture errors in an application and if your application is localized, these should be too.

Firstly, if you write your own TypeConverter classes you may need to provide validation errors when conversion fails. You should probably use ScopedLocalizableError in this case, since it will allow you to provide default error messages and then override them in specific places.

Secondly, if any of your ActionBeans implement Validatable you will need to produce your own error messages for custom validation failures. Since these errors are likely to be specific to the ActionBean, it is suggested that you use the LocalizableError class. The name of the error message is entirely up to you, but it is suggested that it should start with the action path in order to be consistent with other error message keys.

Localized buttons and labels

Localized buttons and labels are really just a special case of localized field names as described in the 'Localized Error Messages' section above. Stripes provides the following tags for generating form buttons: <stripes:button.../>, <stripes:submit.../> and <stripes:reset.../>, and a single tag for generating form field labels: <stripes:label.../> . While each of these tags allow you to determine the value displayed to the user directly on the page, they also support localized values using field name lookups. At the risk of repeating what was said above, if you have a form like:

<stripes:form action="/security/login.action">
<stripes:label for="username"/>: <stripes:text name="username"/>
<stripes:submit name="login"/> <stripes:reset name="reset"/>
</stripes:form>

then you will probably want to define the following resources in your field name bundle:

# Like this... 
/security/login.action.username=Username 
/security/login.action.login=Log In 
/security/login.action.reset=Clear Form 

# Or Maybe just like this... 
username=Username 
login=Log In 
reset=Clear Form 

If neither of the possible localization resources are available for a given button or label, the tag will then examine both the body of the tag and the value attribute. If the body is non-null and non-empty that will be used, otherwise the value attribute will be used. This means that if you need to use a localization bundle or property names that do not line up with the way Stripes expects them, you can do something like this:

<stripes:submit name="save"><fmt:message bundle="MyBundle" key="button.save"/></stripes:submit>

Pass localized parameters to a layout

If you are using the Layout features of stripes you may want to pass localized values to the layout. See the section Passing additional information to the layout for more information about how to pass arbitrary formatted (including localized) content to a layout.

Localizing other things

Stripes doesn't provide tools to localize your entire application for several reasons:

  1. That's a lot of work (wink)
  2. People like to localize in different ways (e.g. one localizable JSP vs. one JSP per locale)
  3. The JSTL formatting tags integrate very nicely with Stripes as a default option

Using stripes with the JSTL formatting tags provides a neat and more complete localization solution. There are a couple of things to take into account.

Firstly, there is absolutely nothing wrong with using the same resource bundle(s) to provide Stripes with localized resources and to provide localized resources through the JSTL tags. There are no issues with doing this, so for example, if you need field names displayed in other places on the page, there's no reason not to access the StripesResources bundle (or whatever you are using). Configure the JSTL localization by specifying the name of the stripes resource bundle in your web.xml:

<context-param>
    <param-name>
        javax.servlet.jsp.jstl.fmt.localizationContext
    </param-name>
    <param-value>StripesResources</param-value>
</context-param>

Secondly, you should rarely have a need to use the <fmt:setLocale.../> tag. Because of Stripes' strategy of using a filter and a request wrapper, all components in your application, including the JSTL tags, will always get the right locale when they call request.getLocale().

One exception is that <fmt:message> does not call request.getLocale()(1). In the case that a request sends no accept-language headers, you will need to include a call to <fmt:setLocale value="${pageContext.request.locale}"/> before <fmt:message>.

(1): http://www.stripesframework.org/jira/browse/STS-676

  • No labels

1 Comment

  1. From the code in v1.5.7 (LocalizedUtility.getLocalizedFieldName(...)), I see that the resource naming "actionPath.fieldName" is deprecated.

    There is a new way not mentioned in this document which is probably meant to be used instead: "fullActionBeanNameWithPackagePath.fieldName".

    Example of the new resource naming

    Assuming my action bean class is "UserManagerActionBean.java", located in package "com.mycompany.user". Let the field name of interest be "userNickname". The expected resource definition in StripesResources.properties would be:

    StripesResources.properties
    com.mycompany.user.UserManagerActionBean.userNickname=How do you want to be called?
Stripes vs. Struts - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

Stripes was born out of my ongoing frustration with the lack of a high quality, easy to use web framework. Sure, Struts has its good points, but there are a lot of small things that really add up. A lot of small things that you learn to work around, and live with, without realizing how unproductive it's making you.

Up until recently it would have been difficult to create a framework that was better-enough to warrant competing with Struts. And with JSF on the horizon (perpetually?), and other web frameworks in play (WebWork, Tapestry, Wicket) some might question the rationale behind yet another framework. But with Java 1.5 and Servlet 2.4 I think the time has come. The rationale is plain and simple - I wanted a web framework that made it easy, no, fun, to write web applications in Java. The best way to demonstrate is perhaps by a comparison with Struts.

Number of artifacts

One of my prime frustrations with Struts is the fact that just to implement a single page/form, I have to write or edit so many files. And I have to keep them in sync, or else things start going horribly wrong. With Struts I have to write my JSP, my Action, my Form, a form-bean stanza in the struts-config.xml, an action stanza in the struts-config.xml, and if I'm going to do it the Struts way, a bunch of forward stanzas. And let's not go into the fact that since this is all stored in one xml file I'm continually facing merge conflicts with my team mates. Yes, there are annotations for Struts, but they are just literal translations of what's in the XML file, and they don't feel natural to me.

Compare this with Stripes. I write my JSP. I write my ActionBean and annotate it with a @UrlBinding to specify the URL it should respond to, and one or more @HandlesEvent annotations to map events to methods. I'm done. All the information about the form and the action is in the same place.

Incremental development

Write a JSP with a Struts <html:form> tag on it and try to preview it in your container before you write your form-bean. Boom! You can't. Exception! Could not find form bean! Now write your form bean. Wait - that's still not good enough. Since everything is hooked together through the action stanza in the XML you better have an action, and edit that too.

Maybe it's just me, but I like to be able to write my JSP, see if it looks ok, then go and write the back end components to go with it. It's not brain surgery, and Stripes lets you do it.

Property binding

Struts lets you use nested properties, and that's great. But say you have a property on your Form 'person.address.line1'. Struts will try and set it for you, but if person or address is null, you'll be very sad indeed. So you have to pre-instantiate any objects on which you want to use nested properties. This just seems like too much work.

Stripes will instantiate just about anything for you. Just so long as 'person' and 'address' have no-arg constructors Stripes will build them, join them together, set them on your ActionBean and then set the 'line1' property. It'll even instantiate implementations for any of the Collections interfaces, which leads us to...

Indexed Properties

The JavaBean specification is great for the most part, but it is clearly a child of the Java GUI side of the house. Struts' implementation of indexed properties matches the JavaBean specification. But in a disconnected web model, your Action won't know (and shouldn't have to know) how many indexed properties are coming back. To make it work you end up coding "smart" indexed getter and setter methods that extend lists and insert objects just to keep Struts happy.

Stripes does it all for you. You provide a getter and setter that use a List or Map type, using generics to inform Stripes what type of thing should be in the List or Map. Stripes will instantiate the List or Map for you if necessary, extend it when needed, manufacture new objects and put them in the list and, you know, set properties of objects in the list.

Validation

Other frameworks take shots at Struts for Validation. It's just fundamentally in the wrong place. Forget that the Struts Validator requires yet another XML file with yet more information about your Form that has to be kept in sync. Validation in Struts is divorced from Type Conversion. This is just plain wrong. Why validate that something is a well formed date, then spend a whole bunch more effort converting it to one? Well Struts almost does that. It expends a lot of effort validating it, tosses it all away and then performs some pretty weak type conversion. It's inefficient, and a pain to use. Which is why most of the Struts projects I've seen steer clear of Struts Validator.

In Stripes validation is tied closely to type conversion. A number of commonly used validations can be applied pre-conversion using a simple annotation. This includes things like required field checks, length checks, regex checking etc. Then type conversion happens, or tries to. If it fails, type converters produce validation errors that can be displayed to the user. So you can put your effort into creating good type converters (if the built in ones don't cover your needs) and be done.

Null mean Null (or does it)

Ask yourself this. You have an int property in your form bean and the user doesn't enter a value in a text box. What should your property be set to? Zero sounds like a sensible default right? Now, what if you have an Integer property? If the user doesn't submit anything I'd say that means null wouldn't you? Apparently Struts thinks it means zero again. And if there are validation errors, the form will even repopulate with the value zero! The only way to get around this is to declare your number properties as Strings and do your own conversion (which is a lot of effort if your dealing with multiple Locales).

Stripes takes the position that the HTML specification is a little inconsistent, and does what it can to make it more consistent for application developers. As a result, empty strings are treated the same as if the field didn't get submitted. The user didn't enter a value, so why should your ActionBean receive one and have to figure out what it means?

Formatting

The Struts FAQ says this (among other things) in answer to why the Struts tags provide so little formatting:

Struts on why formatting in tags doesn't exist

First, work started on the JSTL and we didn't want to duplicate the effort.

Second, work started on Java Server Faces, and we didn't want to duplicate that effort either.

Third, in a Model 2 application, most of the formatting can be handled in the ActionForms (or in the business tier), so all the tag has to do is spit out a string. This leads to better reuse since the same "how to format" code does not need to be repeated in every instance. You can "say it once" in a JavaBean and be done with it.

Great. JSTL doesn't have form tags, so while JSTL is great, it doesn't really help here. JSF and Struts don't mix so well even now, and that's a reason to leave people without formatting for years? And the last is just a cop-out. I want to be able to format data in the right way for each page. And it needs to be localizable etc.

Stripes provides high quality formatting support using the same java.text APIs that the JSTL formatting is built on. It's as similar to JSTL formatting as it can be, while being constrained to living on form tags (i.e. we can't have a separate tag for formatting a date vs. a number and so on).

Multi-Event Actions

If you want to have a form that submits multiple different events in Struts you have to either extend the DispatchAction or write your own support for it. And since the DispatchAction requires all buttons to have the same name, and uses the value to determine the method to invoke, it's a huge pain if you're using localized or even just externalized values for your buttons.

Stripes uses the name of the button itself, and has built in support for multi-event Actions. You can localize to your heart's content, and Stripes will detect which button was pressed and invoke the right method for you.

JSP / View Helpers

Struts doesn't really provide a good pattern for providing dynamic data to JSPs that are not the result of another Action. This leaves you with the options of writing a pre-action for the page or doing something outside of Struts. Then, what if another Action wants to forward to your page after it's completed processing. Do you chain the actions? Do you make the second action meet the dependencies of the page also?

Stripes has a neat way of handling this. A custom tag allows the use of ActionBeans as view helpers. It works similarly to the jsp:useBean tag in that if the ActionBean already exists it just gives you a reference to it. If it doesn't exist, the tag will bring it into existence, bind data out of the request on to it, and get it ready for use.

"HTML" Tags

Maybe it's just me who could never get used to it, but why do the Struts form input tags use 'property' instead of 'name'? And why is it 'styleClass' instead of 'class'? It also makes it hard to change a tag back and forth from a plain HTML tag to a Struts tag.

Stripes takes pains to make all the form input tags as close to (if not identical to) their HTML counterparts as possible.

  • No labels
Intercept Execution - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

Stripes includes an Interceptor system designed to make it easy to add functionality to Stripes. For cross-cutting behaviour it is often simpler to write an interceptor than to extend Stripes' built in components.

The following classes form the core of the Stripes lifecycle and interceptor system:

  • LifecycleStage is an enum that describes the stages through which a request progresses. This is discussed in great detail in the Lifecycles Etc. page.
  • Interceptor defines the interface contract that interceptors use.
  • Intercepts is an annotation used to mark interceptors with the stages that they will intercept
  • ExecutionContext wraps up all the context associated with an ActionBean invocation, and is supplied to interceptors

An Example Interceptor

Let's look at an example. The following is a simply interceptor that "logs" a message before and after every lifecycle stage:

NoisyInterceptor.java
@Intercepts({LifecycleStage.ActionBeanResolution,
    LifecycleStage.HandlerResolution,
    LifecycleStage.BindingAndValidation,
    LifecycleStage.CustomValidation,
    LifecycleStage.EventHandling,
    LifecycleStage.ResolutionExecution})
public class NoisyInterceptor implements Interceptor {
    public Resolution intercept(ExecutionContext ctx) throws Exception {
        System.out.println("Before " + ctx.getLifecycleStage());
        Resolution resolution = ctx.proceed();
        System.out.println("After " + ctx.getLifecycleStage());
        return resolution
    }
}

While this interceptor does nothing of practical value, it does illustrate some basic concepts. Firstly the @Intercepts is used to specify what lifecycle stages are intercepted. In this case we specify all stages, so the interceptor will be invoked up to six times during every request to an ActionBean! Secondly, interceptors intercept around the lifecycle stage. As a result they can execute code before it and after it. When the interceptor is ready to execute the lifecycle code (and/or any downstream interceptors) it simply calls ExecutionContext.proceed().

In fact interceptors can decide to skip a stage entirely, or even abort execution early by returning a Resolution. When an interceptors does this, the resolution is executed and the rest of the lifecycle is omitted.

Configuring Interceptors

Configuring interceptors is fairly straightforward. With Stripes 1.5 and up, your interceptors will automatically be detected if they are in a package configured with the Extension.Packages parameter. You can also use an initialization parameter of the Stripes Filter:

"Configuring Interceptors in the web.xml (Stripes 1.5 and up)"
<init-param>
    <param-name>Interceptor.Classes</param-name>
    <param-value>
        com.myco.NoisyInterceptor
    </param-value>
</init-param>
</filter>

Normally, you would only use the web.xml configuration method if the ordering of the interceptors is important. When multiple interceptors intercept at the same lifecycle stage they are executed in the order they are listed.

If you are using Stripes 1.4.x or earlier, you must use the web.xml configuration, and the only gotcha is that Stripes by default configures a single interceptor, the BeforeAfterMethodInterceptor. When specifying interceptors you need to also specify the BeforeAfterMethodInterceptor unless you wish to disable it:

"Configuring Interceptors in the web.xml (Stripes 1.4.x and earlier)"
<init-param>
    <param-name>Interceptor.Classes</param-name>
    <param-value>
        com.myco.NoisyInterceptor,
        net.sourceforge.stripes.controller.BeforeAfterMethodInterceptor
    </param-value>
</init-param>
</filter>

Another Example: Security

Another example of where an interceptor might be useful is in application security. A prototype interceptor might look like:

SecurityInterceptor.java
@Intercepts(LifecycleStage.HandlerResolution)
public class SecurityInterceptor implements Interceptor {
    /** Intercepts execution and checks that the user has appropriate permissions. */
    public Resolution intercept(ExecutionContext ctx) throws Exception {
        Resolution resolution = ctx.proceed();
 
        if (isPermitted(ctx.getActionBean(), ctx.getActionBeanContext()) {
            return resolution;
        }
        else if (loggedIn(ctx.getActionBeanContext()) {
            return new RedirectResolution("/security/Unauthorized.jsp");
        }
        else {
            return new RedirectResolution("/security/Login.jsp");
        }
    }
 
    /** Returns true if the user is logged in. */
    protected boolean isLoggedIn(ActionBeanContext ctx) {
        return ((MyActionBeanContext) ctx).getUser() != null;
    }
 
    /** Returns true if the user is permitted to invoke the event requested. */
    protected boolean isPermitted() { ... }
}

In this case the interceptor intercepts at one specific lifecycle stage: handler resolution. When this stage is complete the ExecutionContext contains information about which ActionBean is being called, and which event is being handled - sufficient information to determine if the user is allowed access or not. On top of this, the interceptor can, through the ActionBeanContext, access information in the HttpServletRequest for further checks.

The BeforeAfterMethodInterceptor

The BeforeAfterMethodInterceptor mentioned above is an interceptor which gives ActionBeans the ability to define methods to be execute before and/or after certain lifecycle stages. Methods marked with a @Before are run before the specified lifecycle stages. If no stage is specified then the method is run before the EventHandling stage. Methods marked with an @After annotation work similarly, but run after the specified lifecycle stages.

The SpringInterceptorSupport class

The SpringInterceptorSupport class is a simple base class that provide Spring bean injection into custom interceptors. Otherwise there is no difference between extending SpringInterceptorSupport and implementing Interceptor directly.

  • No labels
Lifecycles Etc. - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

ActionBean/Request Lifecycle

The request lifecycle for a request that targets an ActionBean (i.e. is resolved to the Stripes Dispatcher Servlet) is fairly complicated. At a high level it can be seen as:

  1. Resolve an ActionBean based on the URL of the request and set the ActionBeanContext on it
  2. Resolve the Handler method that will handle the event received in the request
  3. Bind properties from the HttpServletRequest into the ActionBean, running validation as necessary
  4. Invoke any custom validation methods
  5. Invoke the appropriate handler method on the ActionBean
  6. If the ActionBean returns a non-null Resolution, execute it

But this is a high level view of what happens, and misses a lot of details. The following sections expand on this and provide those details.

Stage 0: Preprocessing by the Stripes Filter

In a correctly configured web application all requests to Stripes, and for JSPs, are routed through the Stripes Filter which provides several necessary services.

Firstly it "hides" the current Configuration somewhere, so that anywhere in the application the Configuration can be retrieved by calling StripesFilter.getConfiguration()

Secondly it resolves the Locale that should be used for the current request. It does this by invoking the configured LocalePicker.

Thirdly it wraps the HttpServletRequest with a StripesRequestWrapper. The wrapper ensures that HttpServletRequest.getLocale() always returns the picked Locale. The wrapper is also responsible for detecting when the request is a multipart/form-data request and correctly parsing such requests to provide access to the request parameters and uploaded files.

At this point the flow of control could flow directly to a JSP. In this case the request lifecycle continues on just like any other JSP request. If the request is for an ActionBean event, then we continue on to the Stripes DispatcherServlet.

Stage 1: Resolving (and Creating) the ActionBean

Firstly the DispatcherServlet fetches the configured ActionBeanContextFactory and uses it to manufacture an ActionBeanContext. The DefaultActionBeanContextFactory looks for a configured class name, and if none is found, creates a new ActionBeanContext.

Safer interactions with Request, Response and Session

Leveraging the DefaultActionBeanContextFactory to supply your own application specific subclass of ActionBeanContext allows you to centralize all of your access to HttpSession, HttpServletRequest and HttpServletResponse in a single place. This has the advantage of making your application much easier to test as you can always substitute a test version of your ActionBeanContext during test runs.

The DispatcherServlet then fetches the configured ActionResolver and uses it to resolve the appropriate ActionBean instance. The default ActionResolver is the NameBasedActionResolver. This resolves an ActionBean instance using the following logic:

  1. Match the URL path of the request to the URL binding of an ActionBean class
  2. If the ActionBean is annotated with @SessionScope then
    • Look for an instance in HttpSession using HttpSession.getAttribute(UrlBinding)
    • If an instance exists return it, otherwise create it, insert it into HttpSession
  3. Else the ActionBean is using the default (and highly recommeded) Request scope
    • Create an instance of the ActionBean
  4. Invoke setContext() on the ActionBean
  5. Insert the bean into the relevant scope (request or session) using the URL binding as the key
  6. Return the ActionBean instance

The DispatcherServlet then inserts the ActionBean into the request scope under the key 'actionBean' for convenience. This allows developers to easily identify the current/last executed ActionBean.

Stage 2: Handler Resolution

In this stage the DispatcherServlet uses the ActionResolver to determine the name of the event submitted. If there was no identifiable event name then the ActionResolver is asked for the @DefaultHandler method, otherwise it is asked for the method which handles the named event.

The event name is then set on the ActionBeanContext. Note that the correct name is set even when the default event is executed.

Stage 3: Binding and Field Validation

The process of binding and validation, while driven by Stripes, allows the ActionBean author to assert a certain amount of control. To better understand this section it is worth understanding the following interfaces and annotations first:

This section will provide an overview of validation as relevant to the ActionBean lifecycle. For more information please read the Validation Reference.

The first step involves the DispatcherServlet looking up the configured ActionBeanPropertyBinder and invoking it to perform field level validation and binding. The ActionBeanPropertyBinder is told by the dispatcher whether or not validation should be performed. The default ActionBeanPropertyBinder is the DefaultActionBeanPropertyBinder.

Even handlers with @DontValidate can produce validation errors

Specifying @DontValidate turns off all optional validation. However, binding, type conversion and validation are all inextricably linked. If a property needs to be type converted to be bound, and the conversion fails, it will produce a validation error. Therefore, if using @DontValidate for events that receive user input, keep in mind that validation errors may still occur.

At a high level the DefaultActionBeanPropertyBinder does the following:

  1. Performs required field validation on all required fields
  2. Perform pre-conversion validations like min/max lenght, mask checks etc.
  3. For each field supplied in the request that had a non-empty-string value
    • Convert the field using the type conversion system
    • Bind the converted values on to the ActionBean
  4. Run post conversion validations including min/max numeric value and expression checks
  5. Return a ValidationErrors containing any errors that arose during validation and binding

Stage 4: Custom Validation

Next the ActionBean is examined to determine which (if any) validation methods should be executed. Validation methods may be specified to run only when no errors have been generated so far, or always. An application level default exists and can be configured; if not configured the default is not to run validation methods when errors exist (this is done so that ActionBean authors can always rely on a consistent, validated ActionBean in validation methods, and not have to continually check for nulls and inconsistent state).

Handling of validation errors

When errors are discovered during validation and the ActionBean implements ValidationErrorHandler the handleValidationErrors(errors) method will be invoked. In this method bean authors may manipulate the collection of errors (perhaps emptying it, which has significant implications) and/or return an alternative Resolution. If a Resolution is returned it is executed immediatley.

At this point if there are no validation errors (because they were removed by handleValidationErrors()) then we skip to the end of validation and binding. If errors still exist we need to execute an appropriate Resolution. ActionBeanContext.getSourcePageResolution() is invoked to fetch the Resolution. In the default ActionBeanContext this returns a ForwardResolution corresponding to the page that originated the request. The Resolution is then executed, resulting the in the same page being re-displayed in the browser, and the form controls getting re-populated and rendered in error.

Stage 5: Executing the ActionBean

Assuming that everything went well up to this point, and no validation errors were created, the DispatcherServlet will invoke the handler method on the ActionBean. If the ActionBean throws an Exception this will be propogated by the DispatcherServlet - either directly if it is a Servlet or Runtime exception, or by wrapping it in a StripesServletException otherwise.

The ActionBean may execute arbitrary code, including handling the response directly - though this is not encouraged. Handler methods may return any Object, but the return is ignored unless it is an instance of Resolution. In this case, if the ActionBean returns a non-null Resolution the DispatcherServlet will call its execute() method to complete the request.

  • No labels
Download - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

Stripes is distributed under the Apache License (Version 2) (older versions of Stripes were released under the LGPL and are still available under that license).

The current version of Stripes is 1.5.8. For details of what changed between versions please refer to the Release Notes.

Stripes Downloads

Downloads are hosted by GitHub. All released files (including all old releases) can be browsed here.

There are three separate distribution files for Stripes:

  1. The regular (non-source) distribution includes the compiled version of Stripes, the Examples application and minimal library dependencies. It also includes source-code for Stripes and the examples, but not a build environment. This is the recommended download for new Stripes users. Download
  2. The bundle is the right choice for developers who already used Stripes and just want the latest release for their web application. It contains the current Stripes library jar and a separate source jar for being used in your IDE. Read the Dependencies section to see what libraries Stripes also needs. This is the recommended download for experienced developers. Download
  3. Due to the desire to keep the distribution light and the size of some of the compile-time dependencies a third, source, distribution is also available. This distribution includes the Stripes source, the build environment and all build dependencies. This is the recommended download for developers that need to build Stripes on their own machine. Download

The latest JavaDoc is browsable here.

Maven Integration

Integrating Stripes is simply done by declaring this dependency:

pom.xml
<dependency>
<groupId>net.sourceforge.stripes</groupId>
<artifactId>stripes</artifactId>
<version>1.5.8</version>
</dependency>

Make sure you add the logging provider of your choice, too.
If you need file upload functionality, simply add one of the following dependencies:

pom.xml
<dependency>
<groupId>com.servlets</groupId>
<artifactId>cos</artifactId>
<version>05Nov2002</version>
</dependency>

or

pom.xml
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<!-- Required by commons-fileupload -->
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>

Library dependencies

Stripes relies on the following libraries to run:

Commons Logging needs a real logging provider (like log4j) in order to work fine.

COS and Commons FileUpload are only needed for file upload functionality. You just need exactly one of them.

  • No labels
Unit Testing - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

This document outlines two major ways to test your ActionBeans and related classes outside of a container like Tomcat, Resin or Jetty. To decide which way is right for you, you should probably read and examine both - neither is a one-size-fits all solution, and you may even want to apply both techniques in your project.

But first, let's get a few things out of the way...

  1. Automated testing is a good thing(tm). Having automated tests lets you refactor ruthlessly, and generally leads to better quality code.
  2. I don't want to get into a philosophical argument about what is and isn't unit test; having well written tests that can be run automatically is good whether they are technically unit tests or not
  3. There are tools out there that will let you test the whole cycle including JSP rendering inside your regular servlet container; I find these are a bit too much like hard work and prefer to test outside of my container when possible
  4. I'd like to personally recommend TestNG to every Stripes user. It's way ahead of JUnit these days, and if you've done any unit testing in your life, your ramp-up time will be about 15 minutes.

As if to prove point number four, all the examples in this document (indeed all the Stripes unit tests) use TestNG.

Approach 1: Invoking your ActionBeans directly

Because ActionBean classes are simply POJOs there's nothing stopping you instantiating, setting attributes on them, and invoking handler methods. Take the following example test of the CalculatorActionBean class from the examples:

CalculatorActionBeanTest.java
public class CalculatorActionBeanTest {
    @Test
    public void myFirstTest() throws Exception {
        CalculatorActionBean bean = new CalculatorActionBean();
        bean.setContext( new ActionBeanContext() );
        bean.setNumberOne(2);
        bean.setNumberTwo(2);
        bean.add();
        Assert.assertEquals(bean.getResult(), 4, "Oh man, our math must suck!");
    }
}

This works really well when ActionBeans are standalone entities. But if we want to handle situations where values are stored in session, or cookies are set using the response, we need to go one further. Hopefully by now you've read the How-To on State Management; if not you might like to skim it now. It explains how to use your own subclass of ActionBeanContext with Stripes to make state management clean, type safe and independent of the Servlet API. Let's imagine we had the following abstract ActionBeanContext subclass to define our interface:

MyAbstractActionBeanContext.java
public class MyAbstractActionBeanContext extends ActionBeanContext {
    public abstract void setUser(User user);
    public abstract User getUser();
}

The concrete implementation of this class that is used by the regular application would look like this:

MyActionBeanContext.java
public class MyActionBeanContext extends MyAbstractActionBeanContext {
    public void setUser(User user) {
        getRequest().getSession().setAttribute("user", user);
    }
 
    public User getUser() {
        return (User) getRequest().getSession().getAttribute("user");
    }
}

So far so good right? Now, if all our ActionBeans are coded against the MyAbstractActionBeanContext class, then they'll accept any subclass of it, not just the MyActionBeanContext class that we'll be using in the regular application. That means we can write a test implementation and substitute that in during testing. Such an implementation might look like:

MyTestActionBeanContext.java
public class MyTestActionBeanContext extends MyAbstractActionBeanContext {
    private Map<String,Object> fakeSession = new HashMap<String,Object>();
 
    public void setUser(User user) {
        this.fakeSession.put("user", user);
    }
 
    public User getUser() {
        return (User) this.fakeSession.get("user");
    }
}

Note that we could have just used an attribute of type User in the test implementation, but if you're going to store more than a couple of objects in session, using a Map is probably easier. Although the above only demonstrates wrapping session, the same pattern can be applied for interaction with any Servlet API, e.g. setting and retrieving cookies, setting request attributes etc. If you mediate all access to Servlet API classes through a custom ActionBeanContext then your ActionBeans can have the best of both worlds - use of the Servlet API when they need it, and complete independence from the Servlet API!

The following shows how we might use this technique to perform a couple of tets on a LoginActionBean:

LoginActionBeanTest.java
public class LoginActionBeanTest {
    @Test
    public void successfulLogin() throws Exception {
        MyAbstractActionBeanContext ctx = new MyTestActionBeanContext();
        LoginActionBean bean = new LoginActionBean();
        bean.setContext(ctx);
        bean.setUsername("shaggy");
        bean.setPassword("shaggy");
        bean.login();
 
        Assert.assertNotNull(ctx.getUser());
        Assert.assertEquals(ctx.getUser().getFirstName(), "Shaggy");
        Assert.assertEquals(ctx.getUser().getLastName(), "Rogers");
    }
 
    @Test
    public void failedLogin() throws Exception {
        MyAbstractActionBeanContext ctx = new MyTestActionBeanContext();
        LoginActionBean bean = new LoginActionBean();
        bean.setContext(ctx);
        bean.setUsername("shaggy");
        bean.setPassword("scooby");
        bean.login();
 
        Assert.assertNull(ctx.getUser());
        Assert.assertNotNull(ctx.getValidationErrors());
        Assert.assertEquals(ctx.getValidationErrors().get("password").size(), 1);
    }
}

This approach is all well and good, but it has several shortcomings:

  • It doesn't test the URL bindings of our classes
  • It doesn't test that the validations and type conversions are working correctly
  • As ActionBeans get more complex your tests look more and more like the container
  • It is hard to test that the Resolutions are working correctly

Approach 2: Mock Container Usage

Starting with version 1.1.1 Stripes comes packaged with a rich set of mock objects that implement a large number of the interfaces in the Servlet specification. These can be used to construct a mock container in which to process requests. While a little more complex, this more closely simulates the environment in which your ActionBeans will execute. It also has the benefit of testing everything you specified in annotations.

The classes in question can be found in the package net.sourceforge.stripes.mock. Over time you may want to become familiar with most of these classes, but for now can focus on just two: MockServletContext and MockRoundtrip.

MockServletContext is a mock implementation of an individual context within a servlet environment. We'll set up a class of this type to accept and process our requests. It takes very little code to get started:

Setting up a MockServletContext
MockServletContext context = new MockServletContext("test");
 
// Add the Stripes Filter
Map<String,String> filterParams = new HashMap<String,String>();
filterParams.put("ActionResolver.Packages", "net.sourceforge.stripes");
context.addFilter(StripesFilter.class, "StripesFilter", filterParams);
 
// Add the Stripes Dispatcher
context.setServlet(DispatcherServlet.class, "StripesDispatcher", null);

In this example we're doing the following:

  • Instantiating a MockServletContext and giving in the name test (i.e. all URLs will start /test )
  • Setting some initialization parameters for the StripesFilter
  • Inserting the StripesFilter into the mock context
  • Registering the Stripes DispatcherServlet with the mock context

At this point you might be asking yourself why you have to replicate these steps? Well, umm, ok. It's only five lines of code without the comments and empty lines. The reason is simply that the mock objects included with Stripes are not tied to Stripes in any way. If you have a couple of custom servlets you wrote (and why would you do that when you're using Stripes? but I digress) you could test those just as effectively using these mock objects.

The initialization parameters supplied to the StripesFilter above are the same as those that would appear in the web.xml for use in a container. You're free to supply identical values, or values more appropriate for testing. It's up to you.

Adding additional filters

You can add any additional filters needed to make sure your ActionBeans function correctly. If you use a Filter to implement the OpenSessionInView pattern, or to roll your own security, you can add it to the context too. Just remember that filters are invoked in the order in which they are inserted into the context.

Being mock objects, and not a full servlet container, there are several limitations of which you should be aware:

  • There's no URL matching; all Filters are applied to every request
  • A MockServletContext supports only a single Servlet, so you can only test on thing at a time
  • Forwards, Redirects and Includes are not processed (but information about them is recorded for verification)

Lastly, before we move on, it's probably a good idea to take the code that generates the MockServletContext and wrap it up in a test fixture. While instantiating a mock context is much cheaper than starting up a full servlet container, it still can take a second or two. That's not much, but if you manufacture one for each of your unit test methods, and you have hundreds of test methods...well, you'll be waiting a while for those tests to run. Such a test fixture could be implemented either as a regular class which lazily creates a single mock context and provides it to any test that needs it, or by annotating the method that generates the context with TestNG's @Configuration(beforeSuite=true) annotation. For example:

TestFixture.java
public class TestFixture {
    private static MockServletContext context;
 
    @Configuration(beforeTest=true)
    public void setupNonTrivialObjects() {
        TestFixture.context = new MockServletContext("test");
        ...
    }
 
    public static MockServletContext getServletContext() {
        return TestFixture.context;
    }
}

Now, on to the interesting bit - writing some actual tests. While you're certainly welcome to go about instantiating and using all the classes in the mock package directly, it's much easier to use MockRoundtrip instead. MockRoundtrip acts as a facade to several other mock objects and introduces some knowledge of how Stripes works in order to make things as simple as possible. The following is a basic example of a test using MockRoundtrip:

Simple tests using MockRoundtrip
@Test
public void positiveTest() throws Exception {
    // Setup the servlet engine
    MockServletContext ctx = TestFixture.getServletContext();
 
    MockRoundtrip trip = new MockRoundtrip(ctx, CalculatorActionBean.class);
    trip.setParameter("numberOne", "2");
    trip.setParameter("numberTwo", "2");
    trip.execute();
 
    CalculatorActionBean bean = trip.getActionBean(CalculatorActionBean.class);
    Assert.assertEquals(bean.getResult(), 4.0);
    Assert.assertEquals(trip.getDestination(), "/index.jsp");
}

There's quite a lot going on in that little snippet. First we fetch the MockServletContext from the test fixture. Next we instantiate a new MockRoundtrip passing it the context and the ActionBean class we want to invoke. The context is used to help generate the URL for the request, and is used later on to process the request. The ActionBean class is used solely to grab the URL from the @UrlBinding annotation. If you prefer, there are alternate constructors that take String URLs instead. Also, since we didn't pass in a MockHttpSession, the MockRoundtrip will create one for us. For larger/longer tests you might like to create a MockHttpSession and use it for several requests to mimic a real session.

After the MockRoundtrip is created, we set two request parameters on it. Notice how they are both Strings just like any regular request parameter. If you need to supply more than a single value for any parameter you can just supply additional values - the setParameter() and addParameter methods are vararg methods.

trip.execute() executes the request in the servlet context that was supplied when we constructed the MockRoundtrip. This will (assuming our test works) invoke the default operation on the CalculatorActionBean. There is an alternative execute() method that takes an event name as a parameter, and will format the request as if the requested event had been submitted.

Lastly comes verification. We fetch the ActionBean instance from the MockRoundtrip. To be clear: this is an ActionBean that was instantiated by Stripes and just handled a request! The first assertion is quite simple. It checks to ensure that the correct result was calculated (the default operation is addition). The second assertion checks that the ActionBean forwarded or redirected the user to the correct page. One important thing to note is that the MockRoundtrip will paths used in forwards and redirects into web-application-relative paths. You'll get the same path back whether the request resulted in a forward or a redirect, and it will never include the context path. This just makes for easier testing if you switch back and forth between the two resolutions.

Testing failure cases is just as easy:

Testing failure cases with MockRoundtrip
@Test
public void negativeTest() throws Exception {
    // Setup the servlet engine
    MockServletContext ctx = TestFixture.getServletContext();
 
    MockRoundtrip trip = new MockRoundtrip(ctx, CalculatorActionBean.class);
    // Omit first parameter - we could also have set it to ""
    trip.setParameter("numberTwo", "abc");
    trip.execute();
 
    CalculatorActionBean bean = trip.getActionBean(CalculatorActionBean.class);
    Assert.assertEquals(bean.getContext().getValidationErrors().size(), 2);
    Assert.assertEquals(trip.getDestination(), MockRoundtrip.DEFAULT_SOURCE_PAGE);
}

This looks very similar, except that we are omitting a required parameter and supplying "abc" for a numeric field. The first assertion checks to make sure that we have two entries in ValidationErrors. The structure of ValidationErrors is a Map of fieldName to List<ValiationError>. If we wanted to be doubly sure we could fetch the list of errors for each field and make sure each field got an error.

The second assertion checks that the request resulted in navigation back to the originating page. Stripes requires requests to supply the path to the page from which they came (at least if the request could generate validation errors). This is usually taken care of by the <stripes:form> tag, but in this case the MockRoundtrip inserted a default value for us. If we cared about the value, we could set it with MockRoundtrip.setSourcePage(String url).

Lastly, we might have handler methods in ActionBeans that stream data back instead of sending the user to a JSP. For example, the following exerpt from the AJAX CalculatorActionBean:

Handler method that streams data to the client
@HandlesEvent("Addition") @DefaultHandler
public Resolution addNumbers() {
    String result = String.valueOf(numberOne + numberTwo);
    return new StreamingResolution("text", new StringReader(result));
}

In this case we stream back a tiny amount of data - a single floating point number. But the principle is the same as if we streamed back reams of XML or JavaScript. We can test this as follows:

Testing ActionBeans that stream output
@Test
public void testWithStreamingOutput() throws Exception {
    MockServletContext ctx = TestFixture.getServletContext();
 
    MockRoundtrip trip = new MockRoundtrip(ctx, CalculatorActionBean.class);
    trip.setParameter("numberOne", "2");
    trip.setParameter("numberTwo", "2");
    trip.execute("Addition");
 
    CalculatorActionBean bean = trip.getActionBean(CalculatorActionBean.class);
    Assert.assertEquals(bean.getResult(), 4.0);
    Assert.assertEquals(trip.getOutputString(), "4.0");
    Assert.assertEquals(trip.getValidationErrors().size(), 0);
    Assert.assertNull(trip.getDestination());
}

The key line here is the second assertion. This tests that the String output by the ActionBean is exactly equal to "4.0". We could of course test larger strings by checking that they contain certain patterns, or known data. Also MockRoundtrip has a getOutputBytes() method that can be used to retrieve a byte[] of output in case the ActionBean's output was not text.

For more information on using MockRoundtrip and the other mock objects please refer to the javadoc.

MockRoundtrips are compatible with Spring !

Now that you want to use MockRoundtrip with your spring/stripes application, you've got a nice exception :

Exception with Spring
15:01:19,277 WARN DefaultExceptionHandler:90
- Unhandled exception caught by the Stripes default exception handler.
net.sourceforge.stripes.exception.StripesRuntimeException:
Exception while trying to lookup and inject a Spring bean into a bean of type MyActionBean using field access on field private
com.xxx.service.MySpringBean
com.xxx.action.GenericActionBean.mySpringBean

To fix it, you just have to change a little bit your MockServletContext initialisation...

Adding Spring compatibility
private static MockServletContext context;
 
@Before
public static void initContext() {
    Map<String, String> filterParams = new HashMap<String, String>();
    //add stripes extensions
    filterParams.put("Interceptor.Classes", "net.sourceforge.stripes.integration.spring.SpringInterceptor");
 
    filterParams.put("ActionResolver.Packages", "net.sourceforge.stripes");
 
    context.addFilter(StripesFilter.class, "StripesFilter", filterParams);
 
    //here goes your own configuration file
    context.addInitParameter("contextConfigLocation", "/WEB-INF/applicationContext.xml");
 
    // bind your context with an initializer
    ContextLoaderListener springContextListener = new ContextLoaderListener();
    springContextListener.contextInitialized(new ServletContextEvent(context));
 
    // Add the Stripes Dispatcher
    context.setServlet(DispatcherServlet.class, "StripesDispatcher", null);
}

See also :

Wizard action testing using approach 2: Mock Container Usage

Wizard action testing is tricky, so we should make additional efforts to run test for it. You are setting up mockup MockServletContext as usually.

Setting up a MockServletContext
private MockServletContext ctx;
..........
@Before
public void setUpMockServletContext() {
    ctx = new MockServletContext("test");
 
    // Add the Stripes Filter
    Map<String,String> filterParams = new HashMap<String,String>();
    filterParams.put("ActionResolver.Packages", "com.yourpackage.action");
    // filterParams.put("LocalePicker.Locales", ".....");
    // filterParams.put("Extension.Packages", "com.yourpackage.action.extention");
    ctx.addFilter(StripesFilter.class, "StripesFilter", filterParams);
    // Add the Stripes Dispatcher
    ctx.setServlet(DispatcherServlet.class, "StripesDispatcher", null);
}

As we can see we have only one filter in current configuration. We'll use that fact a bit later.

Say you have following fields in your Wizard action bean used on several pages of Wizard process.
The fields are field1, field2, field3.

You can try run your test as before.

Unhandled exception in test method
@Test
public void negativeTest() throws Exception {
    MockRoundtrip trip = new MockRoundtrip(ctx, MyWizardActionBean.class);
    trip.setParameter("field1", "abc");
    trip.execute();
    // ....... the rest of test code
}

Your most probable result is getting error like that.

Unhandled exception message
WARN net.sourceforge.stripes.exception.DefaultExceptionHandler - Unhandled exception caught by the Stripes default exception handler.
net.sourceforge.stripes.exception.StripesRuntimeException: Submission of a wizard form in Stripes absolutely
requires that the hidden field Stripes writes containing the names of the fields present on the form is present
and encrypted (as Stripes write it). This is necessary to prevent a user from spoofing the system and getting
around any security/data checks.

That happens because of Wizard action special processing made by Stripes. The work around is adding following code into your test method.

Work around for Unhandled exception in Wizard action
import net.sourceforge.stripes.util.CryptoUtil;
.......
@Test
public void negativeTest() throws Exception {
    MockRoundtrip trip = new MockRoundtrip(ctx, MyWizardActionBean.class);
    trip.setParameter("__fp", CryptoUtil.encrypt("||field1||field2||field3"));// used for @Wizard action
    trip.setParameter("field1", "abc");
    trip.execute();
    // ....... the rest of test code
}

Field encryption code should start with two leading || and separate all fields by another ||. Then encrypted value is assigned to special parameter "__fp" used internally by Stripes.

Now all your tests pass quit well but most probably you'll find following error in your test log.

Error in Wizard action test log
ERROR net.sourceforge.stripes.controller.StripesFilter - net.sourceforge.stripes.exception.StripesRuntimeException:
Something is trying to access the current Stripes configuration but the current request was never routed through
the StripesFilter! As a result the appropriate Configuration object cannot be located. Please take a look at the
exact URL in your browser's address bar and ensure that any requests to that URL will be filtered through the
StripesFilter according to the filter mappings in your web.xml.

You should add one more JUnit fixture method in your test to work around that issue. You need to remove Stripes filter in your MockServletContext and set it up again for every test method. Add following code to your test and try it again.

Work around second exception in Wizard action log
@After
public void cleanUp() {
    // destroy Stripes filter for every test method
    ctx.getFilters().get(0).destroy(); // assume you have only one (first and single) filter in config
}

We are destroying Stripes filter in internal configuration after every test method. That code assumes you have only one filter in test, so you have them several you should adjust that code for your case.

  • No labels
Formatting Reference - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

Stripes' philosophy when it comes to formatting is pretty simple:

  • Formatting should be easy (duh)
  • Formatting should occur on the page, not in the ActionBean or business code
  • Formatting should be localized
  • Formatting should be easy to extend

To that end, formatting is accomplished using a couple of additional attributes on the set of Stripes tags that have the ability to display data (i.e. form input tags such as text, textarea etc.). Stripes does not supply formatting capabilities outside of form fields - JSTL has a bunch of pretty good formatting tags, and there's no reason to duplicate any of the functionality.

Speaking of JSTL, if you are familiar with the JSTL formatting tags, the Stripes formatting system should feel quite familiar. That's done on purpose. Stripes uses the java.text.DateFormat and java.text.NumberFormat to do most of the actual formatting. The main difference is how you specify formatting parameters - JSTL has specific tags to do formatting of numbers and dates, whereas Stripes has the need to add formatting to a set of input tags.

Using Formatting

You might think that an appropriate first question is how do I use formatting? But you'd be wrong! The Stripes input tags engage the formatting system every time they write a value out to the page. They do this as follows:

  1. Determine the type of object being output (String, Date, Number, Other?)
  2. Ask the FormatterFactory for a Formatter instance that can format the relevant type
  3. If there is a Formatter invoke it to format the object, otherwise use String.valueOf()

Other than the object itself, formatting in Stripes is driven by two parameters (supplied as attributes on the Stripes input tags). The parameters are formatType and formatPattern. formatType is used to specify the type of information which should be output from the source object (e.g. for a java.util.Date object, should it be date, time or both). formatPattern can be either one of a set of named styles, or an explicit pattern as understood by the underlying java.text.Format object. This will all make a bit more sense in the context of the two concrete Formatters supplied with Stripes.

When one or both of formatType and formatPattern are not supplied, the Formatter being used must decide what to do. In the case of the NumberFormatter and DateFormatter included with Stripes they simply use the default java.text.NumberFormat and java.text.DateFormat for the Locale being used for the request.

Formatting Dates

Formatting of Date objects (java.util.Date and subclasses) is done using an instance of DateFormatter. It accepts the following values for formatType:

formatType

Description

date

Output only Date information, and no time information, e.g. Sep 3 2005. (Default)

time

Output only Time information, and no date information, e.g. 18:24.

datetime

Output both Date and Time information, e.g. Sep 3 2005, 18:24

The following named styles (in addition to custom patterns) may be supplied as the formatPattern:

formatPattern

Description

short

Output information in as compact a manner as possible form. Maps to DateFormat.SHORT

medium

Output information in a concise, slightly less compact form. Maps to DateFormat.MEDIUM

long

Output information in a fairly verbose form. Maps to DateFormat.LONG

full

Output information in an extremely verbose form. Maps to DateFormat.FULL

A few things to be aware of:

  • The output format using the named styles can vary according to locale
  • You may use any pattern understood by java.text.SimpleDateFormat in addition to the named styles enumerated above
  • If you specify a format pattern that includes date and/or time segments, this override the formatType parameter - your pattern will be used verbatim

Formatting Numbers

Formatting of Number objects (anything extending java.lang.Number or the primitive number types) is done using an instance of NumberFormatter. It accepts the following values for formatType

formatType

Description

number

Output the value as a regular number (Default)

currency

Output the value as a currency amount, using the symbols for the current locale

percentage

Output the value as a percentage (multiply by 100 and attach the % sign)

The following named styles (in addition to custom patterns) may be supplied as the formatPattern:

formatPattern

Description

plain

Output the value in a manner similar to that produced by Number.toString(). Includes no grouping characters, and will include a decimal separator and decimal digits only if they exist in the number supplied. (Default)

integer

Outputs the value with grouping characters appropriate for the current locale, without a decimal separator and any fractional values. Despite the name (or rather the co-option of integer, by programming languages, to mean something more specific) the integer formatPattern can be used with any numeric type. E.g. 123456789.12 in the en_us locale would be output as 123,456,789.

decimal

Outputs the value with grouping characters appropriate for the current locale, with a decimal fraction at least 2 digits long, and at most 6 digits long. E.g. 1234 in the en_us locale would be output as 1,234.00.

A few things to be aware of:

  • The output format using the named styles can vary according to locale
  • You may use any pattern understood by java.text.DecimalFormat in addition to the named styles enumerated above

Using formatting outside of tags

The only place that Stripes integrates formatting is in the tags. But this doesn't mean that it's the only place you can use it! If you find yourself wanting to format objects somewhere in your Java code, all you have to do is call StripesFilter.getConfiguration().getFormatterFactory() and you have full access to the formatting system.

Extending Formatting

To extend or modify the way formatting is performed will require an understanding of the formatting system. Luckily that's not hard to come by. Stripes uses a configured FormatterFactory to locate instances of the Formatter interface. It's probably worth spending a few minutes reading the javadoc for those interfaces.

Stripes supplies a DefaultFormatterFactory which knows how to supply the built in Date and Number formatters based on the inputs provided. In order to integrate a new formatter, or to substitute a different formatter for Date or Number types, you will have to provide your own implementation of FormatterFactory. This is easily done by subclassing DefaultFormatterFactory and overriding getFormatter() to perform any specific lookups and then delegating to super.getFormatter(). You are, of course, free to directly implement FormatterFactory, just bare in mind that if you do this you will need to handle the lookups for Date and Number types too.

Now all that's left is implementing your own Formatter class. This is a relatively simple operation. Remember that as your inputs you will receive an object to be formatted, a Locale, a formatType and a formatPattern. You may choose to use, or ignore as many of these parameters as you see fit. The following is an example of a custom Formatter for formatting social security numbers:

"SsnFormatter.java"
public class SsnFormatter implements Formatter<Ssn> {
    private String formatType;
 
    /** Sets the format type to be used to render Ssns as Strings. */
    public void setFormatType(String formatType) {
        this.formatType = formatType;
    }
 
    public void setFormatPattern(String formatPattern) {
        // SsnFormatter doesn't use format-pattern
    }
 
    public void setLocale(Locale locale) {
        // Ssn's are US specific, so no locale support either
    }
 
    /** Sets defaults if a formatType was not supplied. */
    public void init() {
        if (this.formatType == null) {
            this.formatType = "lastfour";
        }
    }
 
    /** Formats the Ssn supplied as a String. */
    public String format(Ssn ssn) {
        if ("lastfour".equals(this.formatType)) {
            return "###-##-" + ssn.getLastFourDigits();
        }
        else if ("allnine".equals(this.formatType)) {
            return ssn.getDigits(0,3) + "-" + ssn.getDigits(3,5) + "-" + ssn.getLastFourDigits();
        }
    }
}
  • No labels
Spring with Stripes - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

Stripes is a web application framework that was designed to be easy to use and increase developer productivity. Spring is, primarily, a lightweight component container (although nowadays it is many other things too) designed to be easy to use and increase developer productivity. Naturally you might think to use the two together.

Stripes integrates with Spring to provide your ActionBean classes access to Spring resources in the form of configured Spring beans. It does this using a simple, behind the scenes, injecting of Spring beans into ActionBeans. You'll need to perform a little configuration to get things started, but once that's done you can use your Spring beans in your Stripes web application without writing another line of XML!

Installing and Configuring Spring

This section provides a basic overview of how to install and configure Spring so that you can use it with your Stripes application. It will not cover all the possible ways of doing this, but simply one that works. Start off by downloading the latest version of Spring from http://www.springframework.org/download. Expand the downloaded file and find the file dist/spring.jar. Copy this file into your application's classpath, probably under WEB-INF/lib.

Slimming down your classpath

If having lots of classes in your classpath that you may not be using bothers you, you may want to take the time to go through the various spring-* jars in the dist directory and assemble the minimal set of classes you need to use Spring. Me, I'm too lazy to do that so I just use spring.jar which includes everything I need and a bunch more too.

Once you have the jar or jars in place you'll need to create a spring context file, and configure a web application context in your web.xml file. Let's start by taking a look at the web.xml:

Configuring Spring in the web.xml
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
 
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring-context.xml</param-value>
</context-param>

The listener stanza simply configures a Spring listener class, which is used to bootstrap Spring in the web application environment. The second stanza provides Spring with the context parameter it uses to locate the context file being used. In this case we are using /WEB-INF/spring-context.xml. Any name can be used, but it is generally a good idea to put it in WEB-INF or a subdirectory thereof, as this will ensure it is not served up to clients of your web application!

The second thing we need to do is configure the Spring context. For more details on this you are better off reading the Spring documentation at http://www.springframework.org/docs/reference/index.html. Below is a sample context that we might use to access the various manager components in the Bugzooky application as Spring beans.

/WEB-INF/spring-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
 
<beans>
    <bean name="/bugzooky/BugManager" class="net.sourceforge.stripes.examples.bugzooky.biz.BugManager"/>
    <bean name="/bugzooky/PersonManager" class="net.sourceforge.stripes.examples.bugzooky.biz.PersonManager"/>
    <bean name="/bugzooky/ComponentManager" class="net.sourceforge.stripes.examples.bugzooky.biz.ComponentManager"/>
</beans>

Configuring Stripes for Spring

Now that we have Spring all set up we need to do a couple of things to make Stripes a bit more Spring aware. In your web.xml file locate the initialization parameters for the Stripes Filter. If you do not already have the Interceptor.Classes parameter defined, add the following:

Using the Spring Interceptor (Stripes 1.5 and up)
<init-param>
    <param-name>Interceptor.Classes</param-name>
    <param-value>
        net.sourceforge.stripes.integration.spring.SpringInterceptor
    </param-value>
</init-param>

The parameter tells Stripes to use the Spring interceptor. This will work with Stripes 1.5 and up; if you are still using an older version, you also need to tell Stripes not to stop using the Before/After method interceptor (which is configured by default):

Using the Spring Interceptor (Stripes 1.4.x and earlier
<init-param>
    <param-name>Interceptor.Classes</param-name>
    <param-value>
        net.sourceforge.stripes.integration.spring.SpringInterceptor,
        net.sourceforge.stripes.controller.BeforeAfterMethodInterceptor
    </param-value>
</init-param>

We're done installing things and monkeying with configurations. Now let's do some coding!

Accessing Spring Beans in your ActionBean

Stripes uses the SpringInterceptor to inject Spring beans into ActionBeans after the ActionBean is instantiated. To do this, it must be told how to inject beans and what to inject. As opposed to pushing the linkage out to an XML file, a simple annotation is used. The standard case would look something like this:

Injecting a Spring bean into an ActionBean
/** Setter to allow the BugManager to be injected. */
@SpringBean("/bugzooky/BugManager")
public void injectBugManager(BugManager bm) {
    this.bm = bm;
}

Specifying the exact name and path of the Spring bean you want injected is probably the safest way to go. There are, however, two other options available to you; we'll call auto-wire by name and auto-wire by type. If you omit the value of the @SpringBean annotation thusly:

Auto-wiring of Spring beans in an ActionBean
/** Setter to allow the BugManager to be injected. */
@SpringBean
protected void setBugManager(BugManager bm) {
    this.bm = bm;
}

then Stripes will first attempt to auto-wire by name, and then by type. First the name of the desired bean is derived from the method name - if the method name starts with 'set' then it is removed and the next character down-cased (following standard JavaBean rules), otherwise the entire method name is taken verbatim. In the example above this yields bugManager. The Spring context is then queried to see if it contains a bean called bugManager. This query is case insensitive and path insensitive, resulting in a match to the /bugzooky/BugManager bean in the example above.

If the context does not contain a bean with a matching name then auto-wiring by type is a last ditch effort. In this case if a single bean exists in the context that is of the type required by the annotated method (BugManager in this case) then that bean will be selected and injected into the ActionBean.

It's worth noting that although ActionBeans themselves are not Spring beans, using this approach, the beans injected from the Spring context are completely managed by Spring and have access to all the Spring services any other Spring bean would.

Public Get/Set Methods for Spring Beans are Dangerous

Because Stripes maps parameters from the request into properties on ActionBeans by name, it is extremely unwise to provide public methods matching JavaBean property semantics for Spring Beans (e.g. public void setBugManager(...)). Doing so would allow malicious users to set your Spring beans to null, and possibly even set nested properties on them if a getter method is present for the Spring bean.

The possibility of this happening is quite remote - a malicious user would have to successfully guess that you were using Spring, the name of the methods used to access Spring Beans, and the properties of your Spring Beans. But it is theoretically possible, and as such you should mitigate the risk (especially since it's easy).

Stripes provides several alternatives. If you are explictly naming your Spring beans with @SpringBean("/some/bean") then you can simply name your method differently, e.g. injectBugManager(). If auto-wiring by name, simply drop the set from the method name, e.g. public void bugManager(BugManager mgr). Or alternatively, as discussed below, use protected or private methods and/or field access to inject your Spring Beans.

Method vs. Field Access

Stripes can inject Spring beans through both method access and field access. The access type is determined by the placement of the @SpringBean annotation. If the annotation is directly on the field, e.g.:

@SpringBean private BugManager bugManager;

then Stripes will use field access. If the annotation is on a method, e.g.:

@SpringBean protected void setBugManager(BugManager bm) { ... }

then Stripes will use the method to inject the bean.

If the JVM's security manager will permit it, Stripes will even inject Spring beans, when requested, into protected, package access and private fields and methods. If the JVM's security manager does not permit this, an exception will be raised explaining the problem.

Using Spring Beans in Other Types of Objects

It's sometimes the case that ActionBeans are not the only type of class you'll write that are managed by Stripes but need Spring beans injected into them. Interceptors are a common example, but also if you end up overriding any of Stripes' ConfigurableComponents you may wish to have Spring beans injected.

Fortunately this is fairly easy. In the case of interceptors there is a base class that can be extended. SpringInterceptorSupport is a simple base class that provides Spring bean injection at initialization time.

For other classes, the solution is almost as simple. A class called SpringHelper provides methods to apply the same Spring bean injection mechanism to any object provided you have access to either the ActionBeanContext, ServletContext or Spring ApplicationContext. In most cases this can be acheived by writing:
    SpringHelper.injectBeans(obj, StripesFilter.getConfiguration().getServletContext());

  • No labels
Indexed Properties - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

This how-to will cover how to use both numeric indexed properties and string-indexed or mapped properties. Indexed properties are hard to define, but easy to show by example. Imagine we wanted to edit information about a bug on a page. We might create a form with fields like "bug.name", "bug.description", "bug.priority" etc. Now imagine we want to edit multiple bugs at once, on the same page. We could write a form (and an ActionBean) with an awful lot of properties, like "bug1.name", "bug2.name", etc. but that's way too much work. So instead we use a notation that is understood by Stripes (and lots of other tools), and call our form fields bug[0].name and bug[1].name and so on.

To accomplish this there are two aspects to consider: How to generate the names of the fields on the form and how to code an ActionBean that can receive them. Both are actually quite simple.

Numeric Indexed Properties on the JSP

Essentially constructing the names on the JSP is up to you. This is good and bad news. It means slightly more work for you (the developer) - but it really is only a tiny bit more work. On the upside it means a lot more flexibility. Some of the advantages of this approach are:

  • Indexes can be embedded anywhere inside a property name
  • A single property can have multiple indexing (e.g. bugs[0].watchers[3].name)
  • The source of the index can be anything - allowing interopation with any looping tag that gives you access to it's index, including the c:for* tags, the display:table tag etc.

The following is an example fragment of a JSP using indexed properties (it is a simplified version of a page from the Bugzooky Sample Application):

"Using indexed properties in a JSP"
<stripes:form action="/bugzooky/EditPeople.action">
    <table class="display">
        <tr>
            <th>ID</th>
            <th>Username</th>
            <th>First Name</th>
            <th>Last Name</th>
            <th>Email</th>
        </tr>
        <c:forEach items="${personManager.allPeople}" var="person" varStatus="loop">
            <tr>
                <td>
                    ${person.id}
                    <stripes:hidden name="people[${loop.index}].id" value="${person.id}"/>
                </td>
                <td>
                    <stripes:text name="people[${loop.index}].username" value="${person.username}"/>
                </td>
                <td>
                    <stripes:text name="people[${loop.index}].firstName" value="${person.firstName}"/>
                </td>
                <td>
                    <stripes:text name="people[${loop.index}].lastName" value="${person.lastName}"/>
                </td>
                <td>
                    <stripes:text name="people[${loop.index}].email" value="${person.email}"/>
                </td>
            </tr>
            <c:set var="newIndex" value="${loop.index + 1}" scope="page"/>
        </c:forEach>
        <%-- And now, an empty row, to allow the adding of new users. --%>
        <tr>
            <td></td>
            <td></td>
            <td>
                <stripes:text name="people[${newIndex}].username"/>
            </td>
            <td>
                <stripes:text name="people[${newIndex}].firstName"/>
            </td>
            <td>
                <stripes:text name="people[${newIndex}].lastName"/>
            </td>
            <td>
                <stripes:text name="people[${newIndex}].email"/>
            </td>
        </tr>
    </table>
 
    <div class="buttons">
        <stripes:submit name="Save" value="Save Changes"/>
    </div>
</stripes:form>

It's pretty easy with EL. Using the c:forEach tag the varStatus (which contains the index of the current iteration) is assigned to the name loop. Then, in the form fields the loop.index is inserted into the form field name using EL. E.g. people[${loop.index}].username will translate at runtime into people[0].username, people[1].username etc.

Numeric Indexed Properties using Lists in the ActionBean

This is an area where Stripes really shines. The relevant piece of the ActionBean which corresponds to the above form is:

"Using indexed properties in an ActionBean"
private List<Person> people;
 
@ValidateNestedProperties ({
    @Validate(field="username", required=true, minlength=3, maxlength=15),
    @Validate(field="firstName", required=true, maxlength=25),
    @Validate(field="lastName", required=true, maxlength=25),
    @Validate(field="email", mask="[\\w\\.]+@[\\w\\.]+\\.\\w+")
})
public List<Person> getPeople() { return people; }
public void setPeople(List<Person> people) { this.people = people; }

As you can see, all that is involved is declaring a List (or implementation thereof) property, and providing a getter and setter for the List that have the appropriate generic types. Stripes will introspect your bean at run time and figure out that the "people" property is a list property and will fill in the list for you, instantiating person objects and binding properties to them. You don't even have to instantiate your list - Stripes will do that for you too.

Sets and other non-indexed collections

Stripes explicitly does not support indexed properties for Sets and other collections where there either is no guaranteed ordering, or the ordering is intrinsic to the items in the collection (e.g. SortedSets) - i.e. collections without external indexing. The reason for this is that it is not possible to guarantee consistent ordering in these collections - adding or changing an item can fundamentally alter the ordering of the collection and make numeric indexing invalid, and even dangerous.

For this reason Stripes does not, and will not, support use of Sets as indexed properties directly. Having read the above, if you still want to use a Set the recommended solution is to initialize either a Map (preferably keyed by an id field) or a List in your ActionBean, populate it with the items from the Set and provide a pair of accessor methods for the Map/List. The Map solution is safer since it allows you to access by a stable key as opposed to a numeric index. If you prefer the List approach is it recommended to use Collections.unmodifiable() to ensure that no items can be added or removed from the List, thus ensuring stable ordering.

Validation of Indexed Properties

You might have noticed that the getPeople() method above was annotated, not with validations that make sense for a list, but with validations that make sense for each item in the list - in this case a Person object. Stripes performs validations for each index or row in the list as if it were a regular property. This is true for both numeric/List indexed properties and Map properties, regardless of whether the List/Map is a property of the ActionBean or a sub-property nested within a property of the ActionBean.

For example, imagine we had a page to edit a Person and that the Person has a property 'pets' which is a List of Pet objects, and each Pet has a List of Nicknames. To validate these properties we write the validations without any indexing - just as if they were regular properties:

Specifying validations of indexed properties
@ValidateNestedProperties({
    @Validate(field="phoneNumber", required=true),
    @Validate(field="pets.age", required=true, maxvalue=100),
    @Validate(field="pets.nicknames.name", required=true, maxlength=50),
})
private Person person;

There is, however, one big change in validation. Required field validations are only applied if at least one value with the same index was supplied. To understand this it is easier to think of indexed properties as a mechanism for creating multi-row forms. And this change means that rows in the form that are completely empty are ignored.

For example, in the Bugzooky sample application the Administer Bugzooky page shows the form we have been using as an example above. Look back at the JSP example and you'll notice that an extra row is put in at the end, with no values in it. If the user does not enter anything in this row, the browser will submit it, but because all the fields are empty, Stripes will ignore it and raise no errors. However, if the user entered any field, say username, and left the others blank then a host of validation error messages would show up.

Indexed Properties with Maps (and arbitrary key types)

Just like you can use numeric indices to construct Lists in ActionBeans, you can also construct Maps using any type that Stripes knows how to convert to (e.g. Numbers, Strings, Dates, custom types etc.). Stripes will use the generic information present on the getter/setter for the Map to determine both the type of the key, and the type of the value.

The syntax is very similar to the List example above (in fact with numbers, it is identical). The value between the square brackets should be quoted if it is a String or Character (single and double quotes are both accepted). Otherwise the value can be provided without quotes.

The following example shows how you might use indexed properties with a Map to capture a large set of options or parameters with as little effort as possible. In this case the Map key is the String name of the parameter and the Map value is the numerical value of the parameter.

Map/Indexed properties with String keys in the JSP
<stripes:form ... >
    ...
    <table>
        <c:forEach items="${toolParams}" var="toolParam">
            <tr>
                <td>${toolParam.name}:</td>
                <td><stripes:text name="toolParameters['${toolParam.name}']"/></td>
            </tr>
        </c:forEach>
    </table>
    ...
</stripes:form>

The relevant section of the ActionBean:

Map/Indexed properties with String keys in the ActionBean
private Map<String,Double> toolParameters;
 
public Map<String,Double> getToolParameters() { return toolParameters; }
public void setToolParameters(Map<String,Double> toolParameters) {
    this.toolParameters = toolParameters;
}

Advanced Indexed Properties

There are a few things worth knowing about how Stripes handles indexed properties:

  • Map keys can be any type that Stripes can convert to (either because there is a registered TypeConverter for the type, or because it has a usable public String constructor)
  • The items in Lists and values in Maps can be of any type Stripes can convert to, OR any complex type with nested properties
  • Stripes will happily instantiate Lists and Maps for you (including SortedMaps)
  • Stripes will happily instantiate complex types in Lists or Maps in order to set nested properties, as long as there is a public, no-arg constructor
  • You can chain indexed properties

The last point is worth explaining in more detail. What is means is that you can have ActionBean properties that looks like this:

public Map<Date,List<Appointment>> getAppoinments() { return appointments; }
public void setAppoinments(Map<Date,List<Appointment>> appointments) {
    this.appointments = appointments;
}

and have input fields that look like this:

Note: <stripes:text name="appointments[${date}][${idx}].note"/>

 

  • No labels
Sponsors and Tools - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

Many organizations have donated free products and services to the Stripes project. We'd like to thank the following companies and recognize them for their continued support! We chose to ask these companies for help because we believe they are the best at what they do, and we whole-heartedly recommend them to you.

Atlassian

The friendly folks at Atlassian provide us with free open source licenses to JIRA and Confluence on their new OnDemand cloud. JIRA is the bug tracking tool we use for Stripes and Confluence is the kick-ass wiki that runs this site.

CloudBees

CloudBees provides a free cloud-based Jenkins build environment for the Stripes project.

JetBrains

JetBrains donates free licenses to their Intellij IDEA IDE, one of the best Java IDEs around.

EJ-Technologies

EJ-Technologies provides the Stripes team with free licenses to their excellent profiling tool, JProfiler

Free Tools

We use a number of free tools to support the development and testing of Stripes, and we love them too! They include:

  • TestNG - an innovative testing framework
  • Ant - the build tool that got Java developers away from make-like tools (smile)
  • SourceForge - SourceForge provides subversion, file download and mailing list support
  • No labels
Use Defaults More - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

This page walks through techniques that can be used in Stripes to reduce the amount of configuration (usually in the form of annotation) that is necessary on a per-ActionBean basis.

URL Bindings and Event Names

Starting with Stripes 1.2 Stripes ships with a class called NameBasedActionResolver which is an extension to the default AnnotatedClassActionResolver. The NameBasedActionResolver will automatically create default URL bindings and event names where there are no annotations present. When annotations are present they take precedence.

The set of rules used to generate URL bindings are quite simple:

  1. Take the fully qualified class name
  2. Remove any extra package names from the from (more on this in a minute)
  3. Remove ActionBean or Action or Bean from the end of the class name if present
  4. Replace periods with slashes
  5. Append ".action"

A few examples should make this clearer:

Class Name

URL Binding

com.myco.web.RegisterActionBean

/Register.action

com.myco.www.user.RegisterAction

/user/Register.action

com.companyb.projectb.web.stripes.admin.MainAction

/admin/Main.action

The package name is truncated by removing the packages up until any of a set of "base packages" is encountered. By default that set of packages is: "web", "www", "stripes", "action".

It is possible to extend the NameBasedActionResolver to modify the rules used to generate the URL bindings. Changing the set of packages or the suffix used is trivial. For example:

A customized action resolver
public class MyActionResolver extends NameBasedActionResolver {
    @Override
    public Set<String> getBasePackages() { return Literal.set("ui", "client"); }
    @Override
    public String getBindingSuffix() { return ".do"; /* ugh */ }
}

This will result in mappings like com.companyz.accounts.ui.ledger.GeneralLedgerActionBean to /ledger/GeneralLedger.do. You can completely customize the generation of URL bindings by overriding getUrlBinding(String className).

The NameBasedActionResolver will also map events for all public methods that return a Resolution (excluding those which are annotated with @HandlesEvent). The event name is simply the name of the method in this case.

To see how to configure an alternate ActionResolver take a look at the Configuration Reference.

Automatic Type Conversion

It seems reasonably common to find simple domain classes that are similar to built in Java types, for example Money classes are often wrappers around BigDecimal and EmailAddress classes are often wrappers around a single String. It might be quite tempting to use the Java types in your ActionBean and convert them to your custom types in your handler method. The downside here is repeated code and probably repeated validation annotations.

Now, if you created your own TypeConverter you could use it by attaching a single annotation to your property:

@Validate(converter=MoneyTypeConverter.class)
private Money balance;

But that's sub-optimal! This might seem like the easy route, but when you start using the same Money class in lots of actions you'll have to keep repeating this. Instead you could create your own custom TypeConverterFactory. This will make Stripes aware of the Money type so that it will automatically convert it whenever it sees it. The easiest way to do this is to extend the built in DefaultTypeConverterFactory, for example:

A custom TypeConverterFactory
public class CustomTypeConverterFactory extends DefaultTypeConverterFactory {
    public void init(Configuration configuration) {
        super.init(configuration);
        add(Money.class, MoneyTypeConverter.class);
    }
}

To see how to configure an alternate TypeConverterFactory take a look at the Configuration Reference.

  • No labels
Validation Reference - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

This document describes how valiation works in Stripes, how you specify validations, and how ActionBeans interact with validation. It includes the following sections:

Overview

Validation is conceptually broken into three areas in Stripes:

  • Annotation Driven Validation
  • Type Converstion
  • Custom Validation

Annotation Driven Validation is named thusly because the way the validation rules are specified is through annotations in the ActionBean. These Validations apply to single fields in the form/ActionBean, and encapsulate common and often used validations.

Type Conversion is the process of converting a single form field input from the String value in the HttpServletRequest into a Java object (of any Class, including String). Type conversion is a core part of the validation process, and is easy to extend to new or custom types.

Custom Validation is the name given to the validation performed by arbitrary Java code in one or more validation methods in an ActionBean. An ActionBean can define zero, one or many validation methods and apply them in different scenarios.

Validation Processing Flow

Given the different kinds of validation on offer, one might ask how they are applied, and what happens when errors are produced at any stage. The following is the general flow:

  1. Required field validation is run first
  2. For fields that have no errors after required field validation
    1. Perform minimum/maximum length and mask validations
    2. If the field still has no errors, convert to the target type and bind the property to the ActionBean
    3. If the field converted without errors, run any min/max value checks (for numbers)
  3. For fields that have no errors, evaluate expression based validations
  4. If there are still no errors, run custom validation (unless overridden to run with errors)

This strategy is designed to catch the largest possible number of validation errors possible in a single pass. Thus if the user forgets to enter one value, and mis-types another, they will be presented with both errors the first time they submit the form - but they will not be presented with any additional (e.g. minimum length violation) errors for the field that was required but not supplied. Lastly it should be noted that the reason custom validation is run only when other validations succeed is so that it can rely on the ActionBean being in a well defined. This is, however, configurable (see the Configuration Reference).

As we will see in the sections Selectively Disabling Validation and Changing Validation Outcomes this is not the whole story, but it's good enough for now.

Validation Error Classes

Before we go much further, a quick tour of the different validation error classes is in order. We'll be seeing a lot about validation errors in the next few sections, so it is worthwhile to understand the different types available, and how they are used.

ValidationError is an interface that specifies the methods that all validation error types must support. This makes it easy to have multiple different error classes that offer different functionality. It also means that you can write your own error classes easily if you are not satisfied with the ones that Stripes supplies (hopefully that won't be the case).

SimpleError is, as it says, a simple implementation or ValidationError. It allows a hard-coded message (or one supplied at construction time) to be used, and provides the ability to insert the name and value of the field (and other arbitrary bits of information) into the message.

LocalizableError extends SimpleError with the capability to look up the error message in an external resource bundle. The name of the bundle can be configured - to do this take a look at the Configuration Reference. Looks twice, once with the key prefixed with the action path of the ActionBean being used, and once with the exact key provided.

ScopedLocalizableError extends LocalizableError and provides the ability to perform "scoped searches" for an error message. In reality this just means looking for error messages in the resource bundle with names corresponding to:

  • <actionPath>.<fieldName>.<errorName>
  • <actionPath>.<fieldName>.errorMessage
  • <fieldName>.<errorName>
  • <fieldName>.errorMessage
  • <actionPath>.<errorName>
  • <defaultScope>.<errorName>

In the descriptions below you will see reference to "default scope" and "error name", which refer to how a ScopedLocalizableError is found. All the built in validations in Stripes use ScopedLocalizableError, so that you can specify fairly generic messages at a high level and override them as you see fit.

All of the error messages in Stripes use java.text.MessageFormat which allows you to provide parameters to be merged in to the message. The first two parameters (0 and 1 respectively) are always the field name and field value respectively. An example follows, but check the individual error class javadocs, and the javadoc for MessageFormat for more details:

Example error message specification
myCustomError={0} must be formatted mm/dd/yy. {1} is not a valid date.

Annotation Driven Validation

There are two annotations for adding validations to fields. The @Validate annotation is used to specify validations for scalar or simple properties within an ActionBean. The @ValidateNestedProperties annotation is used to aggregate one or more @Validate annotations in order to specify validations for nested properties of complex types. The annotations can be attached to either the getter or setter methods on the ActionBean. They can also be attached directly to the properties (even private ones).

Each @Validate can specify multiple validations for a single property. The following example demonstrates how to use the annotations:

Using Validation Annotations
@Validate(required=true, minvalue=13, maxvalue=120)
private int age; // annotated right on the field
 
@ValidateNestedProperties({
    @Validate(field="line1", required=true, minlength=5, maxlength=50),
    @Validate(field="line2", minlength=5, maxlength=50),
    @Validate(field="zip", required=true, mask="\\d{5}(-\\d{4})?")
})
public Address getAddress() { ... }

The following sections walk through the syntax of each validation that can be specified with the @Validate annotation.

@Validate(field="...")

The field property is used only when the @Validate annotation is nested within a @ValidateNestedProperties annotation. It is used to specify the property of the complex type to which the validation applies.

@Validate(required=true/false)

Specifies whether or not the annotated field is required when the form is submitted. Defaults to false (so it's never necessary to write required=false even though you can). If a required field is not supplied a single error is generated. The "default scope" is specified as validation.required and the error name is valueNotPresent. This results in an attempt to find error messages in the resource bundle with the following keys:

  • actionPath.fieldName.valueNotPresent
  • actionPath.fieldName.errorMessage
  • fieldName.valueNotPresent
  • fieldName.errorMessage
  • actionPath.valueNotPresent
  • validation.required.valueNotPresent

@Validate(on="...")

The 'on' attribute of the @Validate annotation does not perfom any validation itself, but restricts the application of the required check. To reiterate: it modifies when the required check is applied - not other validations specified at the same time.

By default required field validation is applied to all events not marked with @DontValidate. However, by specifying one or more event names in the 'on' attribute it can be restricted to just those events. For example:

@Validate(required=true, on={"save", "update"}) private String description;
@Validate(required=true, on="delete") private boolean confirm;

The list can also be inverted by supplying event names prefixed with '!'s, for example:

@Validate(required=true, on="!delete") private String description;
@Validate(required=true, on="delete") private boolean confirm;

Note: you cannot mix event names with and without '!'s for the same property, e.g. {"!delete", "save"}.

@Validate(minlength=##)

Specifies the minimum length that the pre-conversion String must be. For example you might specify that a password field must have a minimum length of 6. Doing so would require the password to be six characters in length or greater. If a field fails the minimum length test a single validation error is generated. The default scope is validation.minlength and the error name is valueTooShort. This results in an attempt to find error messages in the resource bundle with the following keys:

  • actionPath.fieldName.valueTooShort
  • actionPath.fieldName.errorMessage
  • fieldName.valueTooShort
  • fieldName.errorMessage
  • actionPath.valueTooShort
  • validation.minlength.valueTooShort

The error message is supplied with an additional parameter (which can be used as {2} in the message text) which is the minimum length constraint that was violated - e.g. 6 in the example above.

@Validate(maxlength=##)

Specifies the maximum length that the pre-conversion String may be. For example you might specify that a username field may have a maximum length of 25. Doing so would require the username to be twenty-five characters in length or less. If a field fails the maximum length test a single validation error is generated. The default scope is validation.maxlength and the error name is valueTooLong. This results in an attempt to find error messages in the resource bundle with the following keys:

  • actionPath.fieldName.valueTooLong
  • actionPath.fieldName.errorMessage
  • fieldName.valueTooLong
  • fieldName.errorMessage
  • actionPath.valueTooLong
  • validation.maxlength.valueTooLong

The error message is supplied with an additional parameter (which can be used as {2} in the message text) which is the maximum length constraint that was violated - e.g. 25 in the example above.

@Validate(minvalue=##)

Specifies the minimum value for a numeric type (after it has been converted to a number). For example you might specify an age field has a minvalue of 0 to enforce non-negative ages! If a field fails the minimum value test a single validation error is generated. The default scope is validation.minvalue and the error name is valueBelowMinimum. This results in an attempt to find error messages in the resource bundle with the following keys:

  • actionPath.fieldName.valueBelowMinimum
  • actionPath.fieldName.errorMessage
  • fieldName.valueBelowMinimum
  • fieldName.errorMessage
  • actionPath.valueBelowMinimum
  • validation.minvalue.valueBelowMinimum

The error message is supplied with an additional parameter (which can be used as {2} in the message text) which is the minimum value constraint that was violated - e.g. 0 in the example above.

@Validate(maxvalue=##)

Specifies the maximum value for a numeric type (after it has been converted to a number). If a field fails the maximum value test a single validation error is generated. The default scope is validation.maxvalue and the error name is valueAboveMaximum. This results in an attempt to find error messages in the resource bundle with the following keys:

  • actionPath.fieldName.valueAboveMaximum
  • actionPath.fieldName.errorMessage
  • fieldName.valueAboveMaximum
  • fieldName.errorMessage
  • actionPath.valueAboveMaximum
  • validation.maxvalue.valueAboveMaximum

The error message is supplied with an additional parameter (which can be used as {2} in the message text) which is the maximum value constraint that was violated.

@Validate(mask=".*")

Specifies a regular expression that the user's input must match. The mask is compiled using java.util.regex.Pattern - if you are not familiar with regular expressions, the documentation for the Pattern class provides a good introduction to the syntax. The entire String submitted must match the mask/pattern supplied, as specified by Pattern.matcher(input).matches().

If the input does not match the mask, a single validation error is output with the default scope validation.mask and the error name valueDoesNotMatch. This results in an attempt to find error messages in the resource bundle with the following keys:

  • actionPath.fieldName.valueDoesNotMatch
  • actionPath.fieldName.errorMessage
  • fieldName.valueDoesNotMatch
  • fieldName.errorMessage
  • actionPath.valueDoesNotMatch
  • validation.mask.valueDoesNotMatch

@Validate(expression="${true}")

Specifies an expression using the JSP Expression Language (EL) to be evaluated. Expressions must return a boolean value. Expression validation is run after all form properties (that have passed validation so far) have been bound into the ActionBean. As a result expressions have access to all the values in the ActionBean!

The value being validated is also available (of course) under the name 'this'. Other named values in the expression are resolved first by looking for an ActionBean property of the same name, and if one is not found, then by then a value is looked for in the request scope, and then the session scope. All the usual EL builtins are available, so it is also possible to access the request parameters etc.

Care should be taken because, while expression validation will not be executed on null or empty values, other values referenced by the expression could still be null. Given the EL's penchant for safely handling nulls, and converting types as needed, this may lead to unexpected outcomes!

Some examples are in order:

// Validate that a number is odd
@Validate(expression="this % 2 == 1") private int oddNumber;
 
// Validate that this number is bigger than the odd number
@Validate(expression="oddNumber != null && this > oddNumber") private long biggerNumber;
 
// Validate that a username is the current user's, or the current user is 'root'
@Validate(expression="context.user.name=='root' || this==context.user.name") private String name;

If the expression does not evaluate to true an error is created with the default scope validation.expression and the error name valueFailedExpression. This results in an attempt to find error messages in the resource bundle with the following keys:

  • actionPath.fieldName.valueFailedExpression
  • actionPath.fieldName.errorMessage
  • fieldName.valueFailedExpression
  • fieldName.errorMessage
  • actionPath.valueFailedExpression
  • validation.expression.valueFailedExpression

@Validate(converter=PercentageTypeConverter.class)

Specifies the TypeConverter that should be used to convert this field. This is not usually necessary as in most cases Stripes will use the right converter for the target type. Two common cases where this is useful are:

  1. When you are using formats that modify the meaning of the data. E.g. formatting numbers as percentages causes 0.95 to be written as 95%. Using the default Double/Float type converter in this case would result in the value being multipled by 100 every time the user edited the value. In this case you could tell Stripes to use the PercentageTypeConverter which does the right thing.
  2. When you are converting to custom types, and have decided not to write your own (very simple) TypeConverterFactory. More on this in the next section.

Since this is not strictly a validation, but a way of modifying the type conversion, it does not produce any error messages.

Type Conversion

Type conversion is quite simply the process of converting the String parameters that are supplied in the HttpServletRequest into the types of the corresponding ActionBean properties. The TypeConverter interface specifies how Stripes will interact with classes that perform type conversion. There are type converters in Stripes for most common types in Java, and adding support for additional types is as easy as coding up a new TypeConverter.

Stripes gets to know about TypeConverters in one of two ways. If an @Validate annotation specifes a converter then that converter will be used, no questions asked. Otherwise Stripes will ask the configured TypeConverterFactory to supply the appropriate TypeConverter for the type being converted to. Extending the DefaultTypeConverterFactory is fairly straight-forward. See the Configuration Reference for details on how to configure Stripes to use your custom TypeConverterFactory implementation.

The following sections document the individual type converters used in Stripes.

BooleanTypeConverter

The BooleanTypeConverter determines that a value is "true" if it matches (ignoring case) any of the values "true", "t", "yes", "y" or "on" or if the value can be parsed as a number and does not equal zero.

Please note that Stripes does not convert and bind parameters submitted in the request with the value "" (empty string). So while you might think that "" would convert to false, in fact conversion is never run, so the value on your ActionBean will be whatever default value is assigned to the property.

Since any String can be converted to a boolean (either false or true), no validation errors are produced.

DateTypeConverter

The DateTypeConverter employs a number of techniques to get the input to parse as a java.util.Date. If the input cannot be parsed into a Date then a single validation error is produced. The default scope is converter.date and the error name is invalidDate.

EmailTypeConverter

The EmailTypeConverter, strictly speaking is not a type converter. It uses JavaMail to parse the String and return a well formatted email address as a String.

A single validation error is produced if the address cannot be parsed. The default scope is converter.email and the error name is invalidEmail.

EnumeratedTypeConverter

The EnumeratedTypeConverter converts Strings representing the names of enumerated type values, back into an instance of the enumerated type. If the value supplied does not match any of the values of the target enumerated type, a single validation error is produced. The default scope is converter.enum and the error name is notAnEnumeratedValue.

Number TypeConverters

Stripes supplied a set of converters for converting to the Java numeric types: byte, short, int, long, float, double and their corresponding wrapper types as well as BigDecimal and BigInteger. They all operate similarly (using one or more java.text.NumberFormats). The converters make a best effort to parse a String as a number. They can handle the following without any additional effort:

  • additional white space
  • currency and non-currency numbers
  • use of the minus sign and parentheses to indicate negation
  • use of grouping characters and decimals (obviously)

The number TypeConverters produce two types of error. The first error is common to all the number TypeConverters; it is generated when the String is not parsable as a number. The default scope is converter.number and the error name is invalidNumber.

The second error is named differently for each number TypeConverter, but means the same; it is generated when the supplied number is out of the range for the target type. The default scope is converter.[byte|short|integer|float] and the error name is outOfRange. For all outOfRange errors two additional parameters ({2} and {3}) are supplied to the error message. The parameters are the minimum and maximum allowable values for the type being converted to, e.g. Integer.MIN_VALUE and Integer.MAX_VALUE.

PercentageTypeConverter

The PercentageTypeConverter converts numbers displayed as percentages (e.g. 95%) into decimal numbers (e.g. 0.95). It requires that the target type is either float, double (or the wrapper types) or BigDecimal. It produces the same error as other number classes, with a default scope of converter.number and the error name invalidNumber.

OneToManyTypeConverter

The OneToManyTypeConverter is a special type converter that converts a single value in the request into a List of values to be bound to the ActionBean. For example, if a user were to enter one or more numbers into a single field, '123, 456 789' that is converted using the OneToManyTypeConverter, a List would be bound to the ActionBean containing three numbers.

The type of the elements in the List is determined based on the declared type in the ActionBean, so if you declare List<Long> you'll get Longs, if you declare List<Boolean> you'll get Booleans. Under the covers the OneToManyTypeConverter will request the appropriate type converter for the individual items, and hence will generate the same validation errors as the LongTypeConverter, BooleanTypeConverter etc.

Custom Validation

Custom validation refers to the execution of arbitrary, custom, validation logic within the ActionBean. ActionBean classes may optionally include one or more validation methods. Validation methods are regular methods that are marked with the annotation @ValidationMethod. These methods are invoked after all other validations have been run and all values have been converted and bound to the ActionBean.

By default validation methods will not be invoked if preceeding validations resulted in errors. Since the ActionBean will be fully populated when validation methods are called it is safe to refer to any property of the ActionBean, or through the ActionBeanContext access values in the HttpServletRequest (though hopefully this will not be common since it makes the ActionBean less testable).

It is possible to configure Stripes to, by default, invoke validation methods regardless of whether or not validation errors exist. To do this refer to the Configuration Reference. Note that by doing this you are no longer guaranteed that all required fields have been supplied or that type conversions and binding succeeded before validation methods are called.

The @ValidationMethod annotation has three attributes that allow the application of the method to be altered, all of which are completely optional. The first is the 'when' attribute which allows the developer to specify whether or not the method should be run when errors exist, regardless of the application default policy. E.g.

@ValidationMethod(when=ValidationState.ALWAYS) public void validateSomething { ... }
@ValidationMethod(when=ValidationState.NO_ERRORS) public void validateSomethingElse { ... }

The 'on' attribute (similar to the 'on' attribute of @Validate) restricts the method to run only on certain events. By default validation methods are run without regard to the event being handled. The 'on' attribute can specify a single event, a list of events, or one or more events preceeded by '!'s. In the last case the meaning is inverted, and the method is executed on all events except those listed. E.g.

@ValidationMethod(on="create") public void one() { ... }
@ValidationMethod(on="update") public void two() { ... }
@ValidationMethod(on={"update","delete"}) public void three() { ... }
@ValidationMethod(on="!delete") public void four() { ... }

The 'priority' attribute is useful only when multiple validation methods exist within an ActionBean and the order in which they are executed is important. Methods with lower (nearer to zero) values for the 'priority' attribute are run before those with higher values.

Lastly, it should be noted that there are two ways to get access to the ValidationErrors object in order to save validation error messages. Firstly, as with any other method in the ActionBean, validation methods can call getContext().getValidationErrors() to retrieve the validation errors. However, they may also take the ValidationErrors objects as a parameter, in which case it will be supplied to the method. E.g.

@ValidationMethod public void validateSomething(ValidationErrors errors) { ... }

Lastly it should be noted that validation methods must be public, must take either zero parameters or one parameter of type ValidationErrors and may return any type, though returns will be ignored.

Performing Validation in Handler Methods

Sometimes it's just not possible (or reasonable) to validate absolutely everything up front in validation methods. Maybe you don't have all the data you need, maybe you won't know if something really is valid until you try and do it! For example, you might not be able to validate that a withdrawal from a bank account is valid until you do it due to concurrency issues (what if two threads check a $100 balance then allow $75 withdrawals?).

In these situations you can create validation errors in your handler method, and send the user back to the page they came from (or elsewhere if you choose). The following is an example:

@HandlesEvent("Withdrawal")
public Resolution withdrawFunds() {
    try {
        getAccount().withdraw( getAmount() );
        return new RedirectResolution("/account/summary.jsp");
    }
    catch (InsufficientBalanceException ibe) {
        ValidationErrors errors = new ValidationErrors();
        errors.add( "amount", new LocalizableError("/account/transaction.action.insufficientFunds") );
        getContext().setValidationErrors(errors);
        return getContext().getSourcePageResolution();
    }
}

Selectively Disabling Validation

Often is it desirable to have validation run for one or more events in an ActionBean, but not for others. For example the "save" method might require validation, as might the "add" method. But the "prepopulate" method might not. While usage of the 'on' attribute of @Validate and @ValidationMethod can acheive this, sometimes it's overly repetitive.

Stripes uses the @DontValidate annotation to make this easier. Any handler method that is annotated with @DontValidate will be invoked directly skipping validation. Type conversion and binding are still executed, and may produce validation errors. However, it is expected that in most (all?) cases the @DontValidate annotation will be used with events whose only input is not user-input (e.g. hidden fields, select lists etc.).

What Happens When There Are Errors?

When one or more validation errors are registered during a request, Stripes will execute the Resolution returned by calling ActionBeanContext.getSourcePageResolution(). By default this returns a ForwardResolution which will forward the request to the same JSP page from which it originated. The page is identified by a request parameter called _sourcePage which is written as a hidden field by the Stripes form tag, and included as a parameter by the Stripes link tag.

To change this behaviour at the global level it is possible to simply override the ActionBeanContext.getSourcePageResolution() method to return a different resolution.

Changing Validation Outcomes

There are times when even the most flexible system cannot give you everything, and you want to manage the validation process at a more detailed level. You can do this by implementing the ValidationErrorHandler interface in your ActionBean. The interface defines a single method handleValidationErrors(ValidationErrors errors) that is invoked when validation fails, but before determining what to do next.

The ActionBean can manipulate the ValidationErrors in any way it sees fit and the changes will be taken into account before proceeding. If all errors are cleared out, it will be as if they never existed! The ActionBean can also use this opportunity to undo any side-affects that might be caused by the binding process if necessary, or to substitute a different Resolution. Check out the ValidationErrorHandler javadoc for more information.

Using the field-metadata tag

Stripes provides field meta data which can be used for client side validation and layout. Here is an example provided by Aaron Porter, using the jQuery JavaScript framework:

(http://article.gmane.org/gmane.comp.java.stripes.user/7575)

(Required scripts available at http://stripes-stuff.svn.sourceforge.net/viewvc/stripes-stuff/JavaScript/trunk/scripts/jquery/)

<s:field-metadata var="fieldMetadata">
$(function(){applyStripesValidation('${fieldMetadata.formId}', ${fieldMetadata});});
</s:field-metadata>

The field-metadata tag provides meta data for fields referenced by input tags. The tag can only provide meta data for fields referenced by tags that appear before its own position in the page code, so usually you'll put it at the end of your form, just before the closing form tag. (If you put it just below opening form tag, for example, no field meta data will be available.) Also, if you have more than one form on your page, be sure to set the form id yourself, otherwise you'll end up with a duplicated form id on your page and validation will only work for one of them.

The tag binds a block of Javascript (actually, JSON) code to a JSP variable, and generates an HTML script block around its body. The body therefore should be Javascript code that makes use of the meta data. Here is also an example of how to use the tag with a Jquery validation plugin:
(http://machak.com/code.html)

Note: visit the above link for updates and improvements. The code below is a skeletal example for illustration:

/**
 *
 * Jquery addapter class so we can use built in Jquery validator.
 * NOTE: this is only skeleton, implementing only small part of possible validation,
 * Usage:
 * &lt;s:field-metadata var="metaData"&gt;
 * $(function(){$.fn.stripesValidation('${metaData.formId}', ${metaData});});
 * &lt;/s:field-metadata&gt;
 * dependancies:
 * jquery.1.3.1
 * jquery.form
 * jquery.validate
 */
(function($){
    /**
     * Processess Stripes meta data and converting it so jquery
     * validation plugin is used for client side validation
     * @param formId id of a form to be processed
     * @param metaData metadata that is produced by stripes tag
     */
    $.fn.stripesValidation = function(formId, metaData) {
        var form = $('#' + formId);
        var mask_count = 0;
    for (var fieldName in metaData) {
        var field = form.find('[name=' + fieldName + ']');
        addValidation(field, metaData[fieldName]);
        // run validation:
        form.validate();
    }
 
    function addValidation(field, obj) {
        for (var prop in obj) {
            debug(prop);
            switch(prop){
                case 'label':
                    break;
                case 'required':
                    if (obj[prop]) { // add only if true
                        field.addClass(prop);
                    }
                    break;
                case 'minlength': // should already be there
                    field.attr(prop, obj[prop]);
                    break;
                case 'maxlength': // should already be there
                    field.attr(prop, obj[prop]);
                    break;
                case 'mask':
                    setMask(field, obj[prop]);
                    break;
                case 'typeConverter':
                    setConverter(field, obj[prop]);
                    break;
                default:
                    debug('missing this:' + prop + ':' + [obj[prop]]);
            }
        }
    }
 
    /**
     * Adds regular expression validation
     * @param field field reference
     * @param mask regular expression mask
     */
    function setMask(field, mask) {
        mask_count++;
        var methodRef = 'maskMethod' + mask_count;
        field.addClass(methodRef);
        $.validator.addClassRules({
            methodRef: {
                methodRef: true
            }
        });
        $.validator.addMethod(methodRef, function (value) {
            return mask.test(value);
        }, 'Value is invalid');
    }
 
    /**
     * Add converter mappings
     * @param field field reference
     * @param converter converter used by stripes for given field
     */
    function setConverter(field, converter) {
        if (converter == 'EmailTypeConverter') {
            field.addClass('email');
        }
        else {
            debug('missing converter mapping:' + converter);
        }
    }
 
    function debug(msg){
        if (window.console && window.console.log) {
            window.console.log(msg);
        }
    }
};
})(jQuery);

 

  • No labels
Wizard Forms - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

Wizard forms (logical forms that span multiple pages) are often problematic. Given the stateless-ness of the web handling interactions that span multiple pages can be quite tricky. A standard example is the new user registration flow. Because the user is required to part with so much information the entries get split across multiple pages. But the information entry should be validated on each page, retained throughout the flow and submitted in one atomic transaction at the end.

Stripes Wizards

Creating wizard flows in Stripes is actually very simple. At it's simplest it involves writing a single ActionBean to manage the flow between pages and marking it with the @Wizard annotation. Beyond that it's up to you whether you want to use a single event/method to handle navigation, or a method-per-event. Using different events (and therefore methods) per page tend to make it easier to manage the flow. An abbreviated example from Bugzooky looks like this:

An example Wizard action bean
@Wizard
@UrlBinding("/bugzooky/Register.action")
public class RegisterActionBean extends BugzookyActionBean {
    @ValidateNestedProperties({
        @Validate(field="username", required=true, minlength=5, maxlength=20),
        @Validate(field="password", required=true, minlength=5, maxlength=20),
        @Validate(field="firstName", required=true, maxlength=50),
    @Validate(field="lastName", required=true, maxlength=50)
    })
    private Person user; // getter/setters omitted for brevity
 
    @Validate(required=true, minlength=5, maxlength=20, expression="this == user.password")
    private String confirmPassword; // getter/setters omitted for brevity
 
    /**
     * Validates that the two passwords entered match each other, and that the
     * username entered is not already taken in the system.
     */
    @ValidationMethod
    public void validate(ValidationErrors errors) {
        if ( new PersonManager().getPerson(this.user.getUsername()) != null ) {
            errors.add("user.username", new LocalizableError("usernameTaken"));
        }
    }
 
    public Resolution gotoStep2() throws Exception {
        return new ForwardResolution("/bugzooky/Register2.jsp");
    }
 
    /**
     * Registers a new user, logs them in, and redirects them to the bug list page.
     */
    @DefaultHandler
    public Resolution registerUser() {
        new PersonManager().saveOrUpdate(this.user);
        getContext().setUser(this.user);
        getContext().getMessages().add(
        new LocalizableError(getClass().getName() + ".successMessage",
        this.user.getFirstName(),
        this.user.getUsername()));
 
        return new RedirectResolution("/bugzooky/BugList.jsp");
    }
}

When an ActionBean is marked as a wizard Stripes does a few extra things for you:

  • When a form is rendered Stripes will insert an encrypted hidden field containing the names of all fields that were rendered on the page (for security reasons, if this goes missing Stripes will complain loudly)
  • At the end of rendering a form, any fields that are present in the request but haven't been rendered in form are written out as hidden fields
  • Required field validation is performed based on the list of fields known to be on the submitting page

The result of this approach is that you can build your ActionBean almost as if the form was really on a single page (you still have to be a little careful to null-check in your custom validations), and move fields from one page to another without having to update your ActionBean or any configuration.

Special Handling of "Start" Events

As mentioned above, Stripes' wizard system does not like it when a request targets a wizard ActionBean and does not supply a valid encrypted hidden field regarding the fields that should be validated. This can be problematic in the case of start events, where perhaps you want to let the wizard ActionBean decide how to initiate the flow (e.g. deciding which registration page to send the user to).

To handle this situation you can designate one or more events to be "start" events. For those events, Stripes will not complain if the encrypted list of fields is not present. As a result, this should be used only for pre-events, not for the first submission of data within a wizard flow. The synxtax (reprising our example from above) is as follows:

Wizard with a start event
@Wizard(startEvents="begin")
@UrlBinding("/bugzooky/Register.action")
public class RegisterActionBean extends BugzookyActionBean {
    ....
    /** Sends the user to the registration page at the start of the flow */
    public Resolution begin() {
        return new RedirectResolution("/bugzooky/Register.jsp");
    }
    ....
}

Backward Navigation Issues

Backward navigation still presents a couple of problems. The automatic-wizard system that Stripes uses assumes that when a submission is made that either all the data on the page is valid, or the user will be shown error messages. Sometimes it may be desirable to allow the user to navigate back in the wizard without forcing validation of the information on the current page.

This is a limitation of the current system. You have three choices, neither of them ideal:

  1. Use JavaScript to perform backward navigation. The downside to this is that by not submitting any data to the server, the user's entries on the current page (if any) are lost.
  2. Force the user to provide valid entries before performing backward navigation
  3. Don't use Stripes' built in required field valiation, but validate fields yourself based on what page is submitted
  • No labels
Stripes vs. Struts2 - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

Here are some quick comparison points between Stripes and Struts2:

 

Stripes

Struts2

Version

1.5

2.0.12

Configuration

web.xml

web.xml, struts.xml, optionally struts.properties and others

Main workhorse

Classes that implement ActionBean

Classes that have an execute() method, optionally implement Action, or extend ActionSupport

Response mechanism

Instance of Resolution

String identifier that maps to a result in struts.xml or in an annotation

View technology

JSP or FreeMarker

JSP, FreeMarker, or Velocity

Layout mechanism

Built-in, with three layout tags. For people who like Tiles or SiteMesh, they can be used as well.

Tiles or SiteMesh

Binding mechanism

Built-in

OGNL

Validation

@Validate and @ValidateNestedProperties

Configure in an XML file, or use annotations

Validation short-circuiting

Built-in, configurable with when=ValidationState.ALWAYS and Validation.InvokeValidateWhenErrorsExist

Set short-circuit="true" on <field-validator>

Custom validation

Annotate your method with @ValidationMethod

Extend either ValidatorSupport or FieldValidatorSupport, and configure in validators.xml

Model-to-view data transfer

${actionBean} attribute

ValueStack

Type conversion

Implementations of TypeConverter<T> (generified)

Implementations of ognl.TypeConverter, typically extensions of StrutsTypeConverter (not generified)

Formatting

Implementations of Formatter<T> (generified)

Implementations of ognl.TypeConverter, typically extensions of StrutsTypeConverter (not generified)

Custom module configuration

Automatically loaded with Extension.Packages init-parameter

Configuration in struts.xml

Interceptors

Implementations of Interceptor, or methods annotated with @Before/@After

Implementations of Interceptor, with configuration in struts.xml

Localization

Resource bundle(s) for errors and field names, and JSTL

Resource bundle search mechanism

Actions

Stripes actions are defined by classes that implement the ActionBean interface, and are automatically loaded by being in one of the packages or subpackages of the packages listed in the ActionResolver.Packages initialization parameter.

Struts2 actions can be plain classes that have a public String execute() method, or classes that implement the Action interface. They must be declared in struts.xml, or automatically loaded with a mechanism copied from inspired by Stripes.

Event handlers

In Stripes, methods of the signature public Resolution methodName() in an action bean define event handlers. Using name= in submit buttons and event= in links with the name of the method targets that method when the action is triggered. Having more than one event handler, and more than one submit button in a form, is simply a matter of defining multiple event handlers and putting the corresponding name in the tag of the submit button.

Struts2 is geared towards having a single event handler method, execute(). You can have other event handler methods with arbitrary names, but you must configure a strategy in struts.xml for mapping a URL to a method name.

Struts2 makes it surprisingly difficult to have more than one submit button in a form. It's doable, but not in as straightforward a manner as in Stripes, as can be seen here.

Resolutions vs. Results

Stripes event handlers return implementations of the Resolution interface. Stripes comes with built-in implementations to forward or redirect a request, stream data, return a JavaScript object, or return an HTTP error code. It's very simple to implement the Resolution interface (it has just one method) to suit custom requirements.

Struts2's execute() methods return a String, which is a symbolic result which must then be mapped to something concrete, either in struts.xml or with an annotation. Arguably, returning a symbolic result and having to go off somewhere else to link the string to a result is an unnecessary burden on the developer. Tim discusses this topic in more detail here.

Custom Type Converters

With Stripes, writing a custom type converter involves implementing the TypeConverter<T> interface, where T is the target type. Then, you can use the type converter for every property of type T simply by putting the type converter in an Extension.Packages package. Alternatively, you can use the type converter for specific properties only by annotating the target property with @Validate(converter=YourTypeConverter.class).

With Struts2, you write a custom type converter by implementing the ognl.TypeConverter interface, usually by extending the StrutsTypeConverter class. Contrary to Stripes, the Struts2 interface is not generified, so your method will return an Object. To use the type converter for every property of type T, you add a line to xwork-conversion.properties with the property being the fully qualified name of T, and the value being the fully qualified class name of your type converter. For a specific property, you add the name of the property and the fully qualified class name of your type converter in ActionName-conversion.properties where ActionName is the class name of the action, and the file is in the same directory hierarchy as the package of the action class.

View Technology

The Stripes framework supports any view technology that supports JSP tag libraries. This means you can use JSP and Freemarker, as both can be implemented as a servlet mapping. JSP is available from J2EE, FreeMarker has to be configured as detailed in FreeMarker with Stripes. Using Velocity with Stripes is possible using the tools project VelocityView, but you will miss the taglib support. Velocity 1.5 doesn't support JSP taglibs – this is a feature on the 2.0 wishlist since 2006.

With Struts2, JSP is supported just like with Stripes. But Struts2 also has plugins to handle both Freemarker and Velocity, thus enabling it's functionality for both these view technologies. That being said, using Freemarker (or JSP) is somewhat more natural than Velocity. For example, for this markup:

<s:form action="Login">
<s:textfield name="username" label="Username"/>
<s:submit value="Submit"/>
</s:form>

The Velocity equivalent is:

#sform ("action=Login")
#stextfield ("name=username" "label=Username")
#ssubmit ("value=Submit")
#end

Interceptors

Interceptors in Stripes are classes that implement the Interceptor interface and specify the lifecycle stage(s) to be intercepted with the @Intercepts annotation. Stripes will automatically load the class via the Extension.Packages mechanism. You can also configure interceptors in web.xml if the order in which interceptors are executed is important.

Struts2 interceptors also implement an Interceptor interface. You must then define the interceptor class in struts.xml, and define a new interceptor stack that uses the default stack and adds your interceptor. Finally, you have to configure which actions will use this new interceptor stack.

  • No labels
Asynchronous Actions - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

This feature is work-in-progress, and available in 1.7.0-SNAPSHOT. Requires a Servlet3+ container.

Description

Async Actions allows handler methods to behave in an asynchronous (Servlet3) fashion. This is mostly used for performance reasons, when a Stripes Action has to can connect to external services (e.g. a web service) in a non-blocking fashion. 

Usage

Async event handlers must return void, and accept a single argument of type AsyncResponse :

signature
public void doSomething(AsyncResponse asyncResponse);

When handling such events, Stripes will start the asynchronous cycle, and let you complete by invoking one of the asyncResponse.complete() variants.

Examples

SimpleAsync
1
2
3
4
5
6
7
8
public class SimpleAsync implements ActionBean {
   
  public void simpleEvent(AsyncResponse asyncResponse) {
    // simple without really anything async...
    asyncResponse.complete(new ForwardResolution("/WEB-INF/stuff.jsp"));
  }
   
}
ReallyAsync
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ReallyAsync implements ActionBean {
   
  public void asyncEvent(final AsyncResponse asyncResponse) {
    // submit to executor service
    ExecutorService executor = ... ;
    executorService.submit(new Runnable() {
       @Override
       public void run() {
            // do anything in a separate thread and complete with a forward
            asyncResponse.complete(new ForwardResolution("/WEB-INF/stuff.jsp"));   
       }
    });
  }
   
}
ReallyAsyncWithLambdas
1
2
3
4
5
6
7
8
9
10
11
12
public class ReallyAsyncWithLambdas implements ActionBean {
   
  public void asyncEventWithLambda(final AsyncResponse asyncResponse) {
    // submit to executor service
    ExecutorService executor = ... ;
    executorService.submit(() -> {
        // do anything in a separate thread and complete with a forward
        asyncResponse.complete(new ForwardResolution("/WEB-INF/stuff.jsp"));   
    });
  }
   
}
NonBlockingClient
1
2
3
4
5
6
7
8
9
10
11
12
13
public class WithNonBlockingClient implements ActionBean {
   
  public void nonBlocking(final AsyncResponse asyncResponse) {
    // call non blocking http client
    httpClient.get('/foo/bar', httpResponse -> {
        // client completed : use http response
        httpResponse.xyz();
        // and complete...
        asyncResponse.complete(new ForwardResolution("/WEB-INF/stuff.jsp"));
    });
  }
   
}
  • No labels
Annotation Reference - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

Contents

Stripes Annotations

Stripes uses Java 5 metadata to allow your classes to define their behavior in the framework without external information, such as is usually stored in XML files. Placing this information directly into your classes reduces the chance of different files becoming out of sync and reduces the duplication and added complexity required of placing behavioral metadata in many locations.

Dispatch Annotations

The following annotations manage the handling requests and their routing to ActionBean implementations.

@UrlBinding

Applies to

ActionBean Class

Example

@UrlBinding("/action/MyAction")

Class

net.sourceforge.stripes.action.UrlBinding

This annotation can be applied to ActionBean classes to override the generated URL binding for the class with a specific URL. It binds the ActionBean to the specified path so that this bean is invoked in response to that path being requested.

@HandlesEvent

Applies to

ActionBean Method

Example

@HandlesEvent("Save")

Class

net.sourceforge.stripes.action.HandlesEvent

This annotation is applied to handler methods in an ActionBean in order to override the event name which the method responds to (by default the same as the method name). The event name as passed into the request is used to determine which event to execute on the bound ActionBean. When there is no provided event, the default handler will be called should one be defined.

@DefaultHandler

Applies to

ActionBean Method

Example

@DefaultHandler

Class

net.sourceforge.stripes.action.DefaultHandler

When applied to an ActionBean method, this annotion denotes that the method should be executed when there is no event name provided in the request. Note: when only one handler method exists, it is deemed to be the default without need for annotation.

@SessionScope

Applies to

ActionBean Class

Example

@SessionScope

Class

net.sourceforge.stripes.action.SessionScope

Causes the ActionBean, the first time it is used within a user's session, to be placed in the user's HttpSession. On subsequent requests the ActionBean will be retrieved from the HttpSession and re-used.

@Wizard

Applies to

ActionBean Class

Example

@Wizard

Class

net.sourceforge.stripes.action.Wizard

Causes submissions to this ActionBean to be treated as part of a wizard form (a logical form split over more than one physical page). Wizards receive special state management and validation handling.

Parameter

Feature

startEvents

one or more event names that Stripes should treat specially as events initiating a wizrard

Validation Annotations

The following annotations support the validation system of stripes, providing metadata about the expected parameters to a request and their boundary conditions.

@DontValidate

Applies to

ActionBean Method

Example

@DontValidate

Class

net.sourceforge.stripes.action.DontValidate

When applied to an event method, this annotation overrides the stripes validation process, skipping the typical required field and field value checks. ActionBean properties are still converted and bound where possible with the use of this annotation.

@Validate

Applies to

ActionBean Member Variable

Example

@Validate(required=true, minlength=5, maxlength=10)

Class

net.sourceforge.stripes.validation.Validate

This annotation defines the rules of validation for a single field in an ActionBean.

Parameter

Feature

required

(true or false) If true, this field must be present or a validation error will be created

on

If specified, restricts the required-field check to the specified set of events

minlength

(#) The minimum length of the parameter string in characters

maxlength

(#) The maximum number of characters in the parameter string

minvalue

(#) The minimum numeric value for a property (applicable to numeric types only)

maxvalue

(#) The maximum numeric value for a property (applicable to numeric types only)

mask

(regex) A regular expression that must be matched to the parameters string

expression

(EL expression) An expression used to validation non-null values for the field

converter

(class) The converter class that will be used to transform this parameter into its object representation

ignore

(true or false) If true Stripes will ignore this property and not bind any input to it

@ValidateNestedProperties

Applies to

ActionBean Member Variable

Example

@ValidateNestedProperties({
@Validate(field="username", required=true),
@Validate(field="password", required=true)})

Class

net.sourceforge.stripes.validation.ValidateNestedProperties

This annotation can be used for complex object validation. For example, if your ActionBean has a User member variable, with a username property and a password property, you can apply individual field validation to each of the sub fields of the object. This validation may only include @Validate definitions.

@ValidationMethod

Applies to

ActionBean Method

Example

@ValidateMethod

Class

net.sourceforge.stripes.validation.ValidateMethod

Marks a method as containing validation logic to be executed before any handler methods are invoked.

Parameter

Description

priority

(int) Specifies the relative priority of the method when multiple validation methods exist

on

If specified, restricts the validation method's application to the specified set of events

when

Controls whether or not the validation method is executed when validation errors exist

Other Annotations

This section contains assorted other annotations used in Stripes

@Before

Applies to

ActionBean Method

Example

@Before(LifecycleStage.HandlerResolution)

Class

net.sourceforge.stripes.action.Before

Causes a method to be run before the lifecycle stage(s) specified. If no stage(s) are specified, runs before event handler execution.

Parameter

Description

value

(LifecycleStage[]) Specifies one or more lifecycle stages before which to run the method

@After

Applies to

ActionBean Method

Example

@After

Class

net.sourceforge.stripes.action.After

Causes a method to be run after the lifecycle stage(s) specified. If no stage(s) are specified, runs after event handler execution.

Parameter

Description

value

(LifecycleStage[]) Specifies one or more lifecycle stages after which to run the method

@SpringBean

Applies to

ActionBean Method

Example

@SpringBean("/biz/SomeBean")

Class

net.sourceforge.stripes.integration.spring.SpringBean

Used to annotated setter methods as accepting injection of a Spring managed bean. Note that the SpringInterceptor must be configured for this annotation to have any affect.

  • No labels
Stripes Injection Enricher - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

Stripes Injection Enricher

https://github.com/StripesFramework/stripes-injection-enricher

Stripes Injection Enricher enriches Stripes objects by satisfying injection points specified declaratively using annotations.
There are three injection-based enrichers provided by Stripes Injection Enricher out of the box:

  • @Resource - Java EE resource injections
  • @EJB - EJB session bean reference injections
  • @Inject - CDI injections

The first two enrichers use JNDI to lookup the instance to inject.
The CDI injections are handled by treating the Stripes object as a bean capable of receiving standard CDI injections.

The @Resource annotation gives you access to any object which is available via JNDI.
It follows the standard rules for @Resource (as defined in the Section 2.3 of the Common Annotations for the Java Platform specification).

The @EJB annotation performs a JNDI lookup for the EJB session bean reference using portable global JNDI names.
If no matching beans were found in those locations the injection will fail.

However you can manually set the JNDI name to lookup using the mappedName attribute for @EJB, as well as the mappedName and name attributes for @Resource:

public class MyActionBean implements ActionBean {
 
    @Inject
    private FooService fooService;
 
    @EJB(mappedName = "java:global/stripes-enricher/business/BarServiceBean")
    private BarService barService;
 
    @Resource(mappedName = "java:comp/env/greeting")
    private String greeting;
    // same as
    @Resource(name = "greeting")
    private String greeting;
 
}

In order for CDI injections to work, the web archive must be a bean archive. That means adding beans.xml (can be empty) to the WEB-INF directory.

Configuration

Maven dependency

Add Stripes Injection Enricher dependency to your project:

<dependency>
    <groupId>com.samaxes.stripes</groupId>
    <artifactId>stripes-injection-enricher</artifactId>
    <version>VERSION</version>
</dependency>

Stripes configuration

Add Stripes Injection Enricher to Stripes Extension.Packages property:

<init-param>
    <param-name>Extension.Packages</param-name>
    <param-value>com.samaxes.stripes.inject</param-value>
</init-param>

Requirements

Stripes Injection Enricher requires a Java EE 6-compliant application server providing support for JSR-299 (CDI).
It has been tested with the following application servers:

  • JBoss AS 6
  • JBoss AS 7
  • GlassFish 3.1
  • No labels
Best Practices - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

While Stripes was designed to provide the flexibility often needed in complex situations, it was also developed to make a number of things extremely easy. This document describes some of the best practices that fit in with the model the author intended for Stripes applications.

Don't duplicate domain objects in ActionBeans

Stripes' binding and validation system was designed from the ground up to make it easy to work with existing domain/value/JavaBean objects because you shouldn't have to duplicate properties from your domain objects in to a presentation specific class just to get things done. Stripes will happily set nested properties within your existing domain objects, many levels deep, and instantiate objects along the way as necessary. So, just embed your domain objects (or Lists, Arrays of Maps of them) in your ActionBean and provide a simple getter/setter pair for the object (or List or Array or Map).

Prefer pre-actions over <stripes:useActionBean ... />

Even if Stripes provides an elegant way to grab a View Helper Action from inside a JSP (or other view), the recommended request/response flow is to use a pre-action. View Helpers can and should be used (via <stripes:useActionBean/>) in order to create reusable JSP fragments, for example, but HTTP requests should generally not be sent to JSPs directly.

Using an ActionBean to handle every incoming request has several advantages:

  • URL Scheme Abstraction
    • Full control over your URLs. Instead of URLs representing the path to your JSP, you can define the URLs either via convention or by specifying them directly using @UrlBinding
    • The ActionBean provides an indirection to the resource : internal JSP refactorings don't impact anything
    • No hard-coded links to resources : you can use <stripes:link beanclass="..."/>, which provides "type-safe" links inside your JSPs
    • Out of the box support for clean, search engine friendly URLs
  • Unified use of Interceptors (Interceptors execute around ActionBeans, not views)
    • Page/action level security
    • HTTP resource cache control
    • Your custom interceptors

Previous experience with other frameworks may have led you to dislike pre-actions due to the amount of work involved constructing them. In Stripes it's actually pretty trivial! In most cases there will need to be an ActionBean to process events (form submission) from a page, in which case it's as simple as adding a one line method to that class which forwards to the page. If the page is read-only it is a little more work (requiring it's own ActionBean) but is still pretty minimal:

public class MyPreAction extends MyBaseActionBean {
    public Resolution view() {
        return new ForwardResolution("/my/view_only_page.jsp");
    }
}

Give the low cost of implementing pre-actions and all the benefits it brings we think it's really worth it!

Implement your own ActionBeanContext subclass

One area where projects often get into trouble, or have unwieldy code, is in accessing attributes store in HttpSession. One goal of Stripes is to ensure that ActionBeans remain independent of the Http* classes, so that they can be easily unit tested. By subclassing ActionBeanContext and configuring Stripes to use your subclass you can provide type safe centralized access to session. And since all access is in one class, it's easy to swap out the implementation with a mock implementation for unit testing. Your class might contain methods like:

public UserProfile getUserProfile() {
    return (UserProfile) getRequest().getSession().getAttribute("user_profile");
}
 
public void setUserProfile(UserProfile userProfile) {
    getRequest().getSession().setAttribute("user_profile", userProfile);
}

Use @Before methods to pre-populate domain objects

When creating screens to edit existing persistent objects it's nice to be able to avoid copying all the properties from the nested domain object to the persistent one that you just looked up from the database. If you pre-populate the object from the database, Stripes will just bind the values from the request like normal, leaving you with an updated object ready to save. Such an implementation might look like:

@Before(stages = LifecycleStage.BindingAndValidation)
public void rehydrate() {
    this.domainObject = getHibernateSession().load(DomainObject.class, context.getRequest().getParameter("id"));
}

Use the <stripes:layout*> tags to create componentized layouts

While Sitemesh is great for applying sitewide decorators and such, sometimes you just want a simpler solution. The layout tags allow you to define simple re-usable layouts using JSPs and then use them to drive the layout of your sites pages. By centralizing your layouts (instead of say, including the header and footer on each page) you make it much easier to change your layout later, perhaps from a standard c-clamp layout to something a little different. And since it's all done with JSP and a couple of custom tags, there's no external configuration or new syntax to learn. See the Tag Library Documentation for more information.

Make sensible use of Stripes' logging facilities

Most classes in Stripes log information that is very useful in figuring out what is going wrong when you are having problems. Stripes logs a lot of information at the DEBUG and TRACE levels, and quite sparse information at INFO and below. In development it is recommended that you either always have Stripes logging at DEBUG level, or be able to switch this on easily. In most logging implementations this can be done by configuring loggers under net.sourceforge.stripes to output at DEBUG level.

However, it should be noted that leaving logging turned up seriously impacts performance! For example, reducing the logging level from DEBUG to INFO on the Bugzooky example app cuts processing time in five in some cases! For this reason you should probably set the log level at INFO or WARNING for production usage.

  • No labels
How To's - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

The following How To's are available for Stripes:

  • No labels
Exception Handling - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

Introduction to Exception Handling

Web Applications can generate exceptions and unfortunately the Servlet specification's built-in exception handling mechanism is somewhat limited. Specifically it can only forward the user to an alternative JSP or file; it cannot forward to a servlet or cause non-JSP code to be executed. This is what the Stripes exception handling mechanism aims to solve.

Most of the major interfaces in Stripes allow developers to throw any exceptions they don't want to handle. Handler methods, validation methods, etc. call all throw Throwable! It's up to you to define your exception handling strategy: Stripes will not impose one on you. That said, it's the author's opinion that you should generally propagate (not catch, wrap and throw) any exception that you can't handle, and let the framework take care of it.

The starting point is the ExceptionHandler interface. Implementations of this interface are used to handle any exceptions that arise during a request. They may rethrow exceptions, or execute arbitrary code and return a Resolution to tell Stripes what to do next.

The ExceptionHandler is invoked from the Stripes Filter. This allows it to handle exceptions generated in ActionBeans, in JSPs and even in other Servlet Filters that are lower down the chain than the Stripes Filter.

Default Exception Handler

The DefaultExceptionHandler used by Stripes doesn't do much! If the exception being handled is a ServletException it is rethrown, otherwise it is wrapped in a StripesServletException and rethrown. That's it!

However, by extending DefaultExceptionHandler, you can easily handle different types of exceptions in different ways. All you have to do is add methods, arbitrarily named, that accept exactly 3 parameters: the exception type, the HTTP request, and the HTTP response. Furthermore, if the method returns a Resolution, it will be executed. For example:

public class MyExceptionHandler extends DefaultExceptionHandler {
    public Resolution handleDatabaseException(SQLException exc, HttpServletRequest request, HttpServletResponse response) {
        // do something to handle SQL exceptions
        return new ForwardResolution(...);
    }
 
    public Resolution handleGeneric(Exception exc, HttpServletRequest request, HttpServletResponse response) {
    // general exception handling
        return new ForwardResolution(...);
    }
}

When an exception is handled the DefaultExceptionHandler looks for the most specific method to handle the exception. If it cannot find one that handles the exact exception type thrown, it will look for a method that handles the parent type and so on up the hierarchy. If no handler can be found, then the exception is rethrown. For this reason it is recommended to always have a handler method that takes Exception.

Delegating Exception Handler

The DelegatingExceptionHandler works similarly to the DefaultExceptionHandler described above, but lets you write more than one class that handles exceptions. Each of those classes must implement the AutoExceptionHandler marker interface.

AutoExceptionHandlers can have one or more methods with the same signature as described earlier:

public Resolution someMethod(Exception e, HttpServletRequest req, HttpServletResponse res);
public Resolution someMethod(NullPointerException npe, HttpServletRequest req, HttpServletResponse res);

When it is initialized the DelegatingExceptionHandler scans the classpath to find all instances of AutoExceptionHandler. These will be found either in the packages configured in the DelegatingExceptionHandler.Packages Stripes Filter init-param, or the more general Extension.Packages.

Writing Your Own Exception Handler

Writing your own exception handler is quite straightforward. The class simply has to implement ExceptionHandler interface, which specifies two methods (including the inherited one). For example:

public class MyExceptionHandler implements ExceptionHandler {
    /** Doesn't have to do anything... */
    public void init(Configuration configuration) throws Exception { }
 
    /** Do something a bit more complicated that just going to a view. */
    public void handle(Throwable throwable, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        TransactionUtil.rollback(); // rollback any tx in progress
        if (AppProperties.isDevMode()) {
            throw new StripesServletException(throwable);
        }
        else {
            request.setAttribute("exception", throwable);
            request.getRequestDispatcher("/error.jsp").forward(request, response);
        }
    }
}

Even easier is to extend DefaultExceptionHandler and add exception-handling methods as described above.

Configuring an Alternative Exception Handler

Having created your own exception handler, you'll need to configure it. You can do this either by placing your class in a package that's configured as an extensions package (see Extensions), or by using the ExceptionHandler.Class parameter, e.g.

<init-param>
    <param-name>ExceptionHandler.Class</param-name>
    <param-value>com.myco.exception.MyExceptionHandler</param-value>
</init-param>

Accessing the ActionBean, ActionBeanContext etc.

The interface of ExceptionHandler is quite basic:

public void handle(Throwable throwable, HttpServletRequest request, HttpServletResponse response) throws ServletException;

This is primarily because the exception handler can handle exceptions from anywhere in a Stripes application - even prior to things like the ActionBean and ActionBeanContext having been created! The interface of AutoExceptionHandler is similar. This may leave you wondering how to get access to the ActionBean etc. should one be present. For example you may know, based on the type of exception being handled, that it had to originate from an ActionBean. Or you might simply wish to handle things differently if an ActionBean is present.

Checking for and retrieving an ActionBean is relatively simple, and through it the ActionBeanContext, ValidationErrors etc. For example:

/**
 * If there's an ActionBean present, send the user back where they came from with
 * a stern warning, otherwise send them to the global error page.
 */
public void handle(Throwable throwable, HttpServletRequest request, HttpServletResponse response) throws ServletException {
    ActionBean bean = (ActionBean) request.getAttribute(StripesConstants.REQ_ATTR_ACTION_BEAN);
 
    if (bean != null) {
        bean.getContext().getValidationErrors().addGlobalError(new SimpleError("You made something blow up! Bad user!"));
        bean.getContext.getSourcePageResolution().execute(request, response);
    }
    else {
        request.getRequestDispatcher("/error.jsp").forward(request, response);
    }
}

 

  • No labels
State Management - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

This document has two major sections:

  1. #Smart State Management using the ActionBeanContext
  2. #Redirect-after-Post using the Flash Scope

Smart State Management

In this section we'll walk through a strategy for how to access HttpSession, Cookies and other HttpServletRequest/Response behaviours in a way that's type safe and keeps your ActionBean classes testable (woohoo!). The key to this is providing your own ActionBeanContext subclass.

Subclassing ActionBeanContext

The following is an example subclass that allows for type safe access to a User object stored in session:

ActionBeanContext subclass
public class MyAppActionBeanContext extends ActionBeanContext {
    public void setUser(User user) {
        getRequest().getSession().setAttribute("user", user);
    }
 
    public User getUser() {
        return (User) getRequest().getSession().getAttribute("user");
    }
}

Now that you have your own ActionBeanContext you'll need to tell Stripes to use it. The easiest way is to drop your class into an extensions package (see Extensions). Alternatively, you can add the following initialization parameter to the Stripes Filter in your web.xml:

Configuring your ActionBeanContext
<init-param>
    <param-name>ActionBeanContext.Class</param-name>
    <param-value>com.myco.MyAppActionBeanContext</param-value>
</init-param>

Using your new ActionBeanContext

Now, the ActionBeanContext that gets set on your ActionBean will always be a MyAppActionBeanContext. You'll still have to cast the context object that you receive in your ActionBean, but thanks to a new feature in Java 1.5 called co-variant return types, you can at least do it once, and not litter your code with casts.

Using a co-variant return type in your ActionBean
public class MyActionBean implements ActionBean {
    private MyAppActionBeanContext context;
 
    /** Interface method from ActionBean. */
    public setContext(ActionBeanContext context) {
        this.context = (MyAppActionBeanContext) context;
    }
 
    /** Interface method from ActionBean, using a co-variant return type! */
    public MyAppActionBeanContext getContext() {
        return this.context;
    }
 
...
}

In Java 1.4 and earlier, the getContext() method would not compile because the interface specifies it must return ActionBeanContext, and the class specifies MyAppActionBeanContext. It'd fail even though MyAppActionBeanContext extends ActionBeanContext. In Java 1.5 this is allowed, and it helps make things a bit cleaner. And if you create your own BaseActionBean class you can put this in there and never have to see it again (smile)

Now that you have your own ActionBeanContext class you can

  • Add type safe methods to access things you need to store in session (hopefully not too many things)
  • Add type safe methods for getting and setting the values of cookies
  • Put all the strings/keys needed for the above operations in one place
  • Access methods and functionality of the request and response objects without coupling your ActionBeans to them

But wait, there's more. Because the ActionBeanContext is attached to your ActionBean, and your ActionBean is dropped into a request attribute, you now have two ways of accessing things stored in session. On a JSP:

<div>${user} == ${actionBean.context.user}</div>

While this may seem trivial, there's one benefit. The first syntax ${user} assumes that you have placed the User object in to a JSP scope (e.g. session) under the attribute key "user". If you ever change that key, say to "__secretPlace_user", your JSPs will break. You're probably less likely to change the type safe methods on your ActionBeanContext, so the ${actionBean.context.user} might be safer. Not a huge benefit, but worth noting.

Enabling Testing

It's often difficult to test classes that live in the web tier. This is usually because they either need to interact with one of the Http* objects, or they are forced to through the API of the Servlet/Action/Whatever model being used. Unit testing of Stripes ActionBeans is already pretty simple (just instantiate, set context, set properties and go), but it falls down when your ActionBean really needs to interact with the request or the session. Use those and suddenly testing is difficult...you might have to have a set of mock Http* objects, etc.

With just a little bit more work, you can make your ActionBeans completely testable. Using our MyAppActionBeanContext example above, we can create an abstract class that our ActionBeanContext will extend, and which our ActionBeans will use. Let's refactor a bit:

MyAppActionBeanContext.java
public abstract class MyAppActionBeanContext extends ActionBeanContext {
    public abstract void setUser(User user);
    public abstract User getUser();
}

Our "real" class would look much the same as before, but perhaps this time we'd name it differently:

MyAppActionBeanContextImpl.java
public class MyAppActionBeanContextImpl extends MyAppActionBeanContext {
    public void setUser(User user) {
        getRequest().getSession().setAttribute("user", user);
    }
 
    public User getUser() {
        return (User) getRequest().getSession().getAttribute("user");
    }
}

And then, we create out test ActionBeanContext:

MyAppActionBeanContextTestImpl.java
public class MyAppActionBeanContextTestImpl extends MyAppActionBeanContext {
    private User user;
 
    public void setUser(User user) { this.user = user; }
 
    public User getUser() {return this.user; }
}

Et voilà, we have a test rig for our ActionBeans that allows us to simulate the real environment with very little effort. You can imagine other ways of doing this. Perhaps the test version (MyAppActionBeanContextTestImpl in our example) could simply extend the "real" version (MyAppActionBeanContextImpl in our example) and override methods that access session or request. You could even go one further and have a protected helper method in your "real" context class called something like setInSession(String key, Object value), which in the real version calls getRequest().getSession().setAttribute(), but in the test version just puts the value in a local Map.

Redirect-after-Post

Redirect-after-Post is the name given to a fairly simple technique. The idea is that after a form POST has occurred and the server has done something significant (create a user, place a trade etc.) that it is safest to then redirect the user to another (or perhaps the same) page. By redirecting we ensure that if the user hits the refresh button they don't re-submit the form. If we were to have simply forwarded to a page after the POST, a refresh would re-post the form!

But there is a down side to redirecting. Anything stored in request attributes is lost when the redirect is issued (because the request for the page is not the same request). One option is to stuff things into session, but that's a bad idea(tm) because things will break when the user has more than one browser window open using your application.

The FlashScope

This is the problem that the FlashScope is designed to solve. In essence the flash scope is a scope much like request scope, session scope etc. Whereas the request scope is defined as existing for the lifetime of a request, the flash scope is defined as existing for the duration of this request and the subsequent request. Any item added to the flash scope will be made available as a request attribute during the current request, and during the next request.

An example of flash scope usage is non-error messages functionality in Stripes. The ActionBeanContext has a getMessages() method that returns a list to which messages may be added. This list is stored in flash scope so that the messages are available to the current request (should the ActionBean forward to a page) and in the next request (should the ActionBean redirect to a page). The code looks like this:

Example usage of FlashScope
public List<Message> getMessages(String key) {
    FlashScope scope = FlashScope.getCurrent(getRequest(), true);
    List<Message> messages = (List<Message>) scope.get(key);
 
    if (messages == null) {
        messages = new ArrayList<Message>();
        scope.put(key, messages);
    }
 
    return messages;
}

The true on the first line tells FlashScope to manufacture a FlashScope if one doesn't yet exist. After that the scope is used just like any other scope - as a container of key/value pairs.

Flashing ActionBeans

By default ActionBeans are not put in flash scope. This is partly for backwards compatibility reasons, and partly because it just isn't necessary to do it all the time! That doesn't mean it's hard to do though. If you're using the standard RedirectResolution it's as simple as chaining one more method call:

return new RedirectResolution("/some/page.jsp").flash(this);

Even if you're redirecting some other way, it's still very easy:

// From within the ActionBean
FlashScope.getCurrent(getContext().getRequest(), true).put(this);

Note: Only do this if you're redirecting to a JSP. If you redirect to an ActionBean that forwards to a JSP, the flashed ActionBean will be overwritten with the ActionBean you redirected to.

Accessing the FlashScope in your JSP

Lets say you called this method in one of your ActionBeans:

public void addRecipe(HttpServletRequest request, String message) {
    FlashScope fs = FlashScope.getCurrent(request, true);
    List<String> messages = (List<String>) fs.get("recipes");
 
    if (messages == null) {
        messages = new ArrayList<String>();
        fs.put("recipes", messages);
    }
 
    messages.add(message);
}

Then you redirected to another ActionBean which forwards to a JSP. This is how you access the FlashScope in your JSP:

<c:forEach var="recipe" items="${recipes}">
    <div class="recipe_name">${recipe.name}</div>
</c:forEach>

As you can see, when you put an object into the FlashScope, it can be accessed directly by its key in an EL statement.

How FlashScope works

The FlashScope works by temporarily storing instances of itself in Session, and removing them on subsequent requests. As a result, when a FlashScope is used, an additional parameter is appended to redirect URLs to tell Stripes which FlashScope to use. Because of this parameter two or more browser windows (or tabs) in the same session will never get confused and access each others' FlashScopes.

An obvious side affect of this strategy is that occasionally FlashScopes get orphaned in session. For this reason Stripes will examine all FlashScopes in existence for a session each time a request is received; each flash scope starts "aging" when the request that generated it is completed, and any FlashScope beyond a certain age (currently 2 minutes) is destroyed. Think of this like session expiration on a smaller scale.

Bringing it all Together

Hopefully the above sections on smart state management and redirect-after-post made sense. The FlashScope actually provides the single best example to date of why using the ActionBeanContext to manage all state related logic is a good idea.

The FlashScope was introduced in Stripes 1.2. Prior to 1.2 the list of non-error messages was stored in a request attribute. Now that the flash scope is available the non-error messages are stored there. Making the change was trivial because ActionBeans were completely isolated from the details of how the messages were stored and retrieved. Had ActionBeans been accessing request (or session) directly the change could not have been made without breaking existing code.

  • No labels
Extensions - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

Extensions - how to extend and customize Stripes

Beginning with Stripes 1.5, you can configure one or more packages in web.xml from which Stripes will automatically load all extensions. Extensions include:

In web.xml, add an initialization parameter to StripesFilter called Extension.Packages. Each package that you indicate automatically includes all sub-packages, so do not use .*. For example,

web.xml
<filter>
    <display-name>Stripes Filter</display-name>
    <filter-name>StripesFilter</filter-name>
    <filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class>
    <init-param>
        <param-name>ActionResolver.Packages</param-name>
        <param-value>net.sourceforge.stripes.examples</param-value>
    </init-param>
    <init-param>
        <param-name>Extension.Packages</param-name>
        <param-value>net.sourceforge.stripes.extensions</param-value>
    </init-param>
</filter>

Now you can drop in Stripes extensions to net.sourceforge.stripes.extensions and its subpackages, and they will automatically be loaded. You can also use more than one package root by separating the packages with commas:

web.xml
<filter>
    <display-name>Stripes Filter</display-name>
    <filter-name>StripesFilter</filter-name>
    <filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class>
    <init-param>
        <param-name>ActionResolver.Packages</param-name>
        <param-value>net.sourceforge.stripes.examples</param-value>
    </init-param>
    <init-param>
        <param-name>Extension.Packages</param-name>
        <param-value>net.sourceforge.stripes.extensions, another.pkg.extensions</param-value>
    </init-param>
</filter>

If you have a class in an extension package and want to tell Stripes not to automatically load it, you don't have to move the class to another package. You can simply annotate the class with @DontAutoLoad.

  • No labels
Display Errors - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

Almost invariably, web applications generate validation errors at some point. And the user has to be told that they did something wrong, and what they should do about it. Hopefully this should be consistent with your applications UI guidelines, and make it clear to the user how to proceed. This how-to explains the tools available in Stripes to help you.

Outputting Errors

Stripes provides a number of tags to output validation errors, and it is worth taking the time to read the tag library documentation for them. The main tag is stripes:errors. This tag can output all the validation errors for a form, or output the errors only for a single field.

I won't repeat the tag documentation here (go read it already!), but here are a couple of examples:

Example 1: Outputting all errors with default formatting
<stripes:errors/>

The above example will, assuming it is not nested inside a <stripes:form> tag, output all the validation errors in an HTML unordered list, for whatever form was posted. If you have more than one form on the page, you can display the errors for each in different places:

Example 2: Multiple forms outputting errors in different locations
<stripes:form action="/foo/first.action">
    <stripes:errors/>
    ...
</stripes:form>
 
...
 
<stripes:errors action="/foo/second.action">
<stripes:form action="/foo/second.action">
    ...
</stripes:form>

The first <stripes:errors/> tag is nested inside a <stripes:form> tag. When this occurs, the errors tag will only output errors if they are the result of posting the form in which it is contained. The second <stripes:errors/> tag is not nested inside a form, but since it is supplied an action attribute, it will only display errors if they are the result of submitting a form with an action matching the value supplied to the errors tag.

The next example shows how to ouput errors for a single field at a time, and output global errors (those not associated to any specific field) separately:

Example 3: Outputting errors by field
<stripes:form action="/foo/fist.action">
    <stripes:errors globalErrorsOnly="true"/>
 
    <table>
        <tr>
            <td>Username:</td>
            <td>
                <stripes:text name="username"/>
                <stripes:errors field="username"/>
            </td>
        </tr>
        <tr>
            <td>Password:</td>
            <td>
                <stripes:text name="password"/>
                <stripes:errors field="password"/>
            </td>
        </tr>
    </table>
</stripes:form>

The default output format for errors is controlled using a set of four resources from the error messages bundle (usually StripesResources). A different set of resources is used when outputting field specific errors to allow distinct presentation. This is documented in more detail in the tag documentation for the errors tag. The format can also be modified on a per-use basis by nesting tags including the individual-error tag inside of the errors tag.

Highlighting the field(s) in error

Stripes also has the ability to modify the rendering of fields that are in error. It does this using a class that implements TagErrorRenderer, which is supplied by a factory class that implements TagErrorRendererFactory. In a few minutes we'll cover what you can do by providing your own implementations of these classes. But first, lets look at what the default classes do.

Changing style with the default TagErrorRenderer

Firstly, there is the DefaultTagErrorRendererFactory which is extremely simple - it returns an instance of DefaultTagErrorRenderer. It can also be configured to return instances of other renderers you might write.

The DefaultTagErrorRenderer is also quite simple. It makes a single change to any field in error; it changes the tag's CSS style. If the tag did not have a class="something" attribute, then the default error renderer adds one, giving class="error". If the tag had a class attribute, say class="foo", then the renderer will rewrite that as class="error foo" which says that the error class has precedence, but anything it doesn't specify may then be specified by the foo class.

It may at first seem a little limiting to have only a single class name for displaying all form fields in error. What if you want to display graphical widgets like selects, check boxes and radio buttons differently from textual widgets like text and textareas? Well, if you asked that question, then it's time to learn a little (more) CSS. CSS selectors allow you to specify styles (with the same name) that select different tags based on all sorts of properties. Probably the most useful in this scenario is the attribute selector. The following shows how to use attribute selectors to apply a global error style to input fields, and then specific changes for some types.

Using selectors to specify error classes
input.error, textarea.error {
    color: red;
    background-color: yellow;
}
 
input.error[type="radio"], input.error[type="checkbox"], select.error {
    background-color: white;
    border: 2px solid red;
}

Using selectors, you could:

  • Specify a different style for every type of input field
  • Specify different styles for when a field is empty vs. has a value that is wrong
  • Specify styles based on fields' position within an HTML layout

    CSS Selector Reference

    The following web page is an excellent reference on CSS selectors: http://www.w3.org/TR/REC-CSS2/selector.html.

Using custom TagErrorRenderers

We've seen that using CSS we can change the appearance of error fields quite significantly. But there are probably some things that we can't do with CSS - or at least would find very very difficult. Perhaps you'd like to put a little red * next to fields in error, or a warning icon (warning), or maybe even a little message like "<<< Hey Moron, error over here". Well, maybe that last one is going a bit far, but who knows what your users want! All of the above, and more, can be done by implementing your own TagErrorRenderer.

Actually the first step is to determine how many TagErrorRenderer implementations you need. In most cases it will probably be just one, but when the TagErrorRendererFactory is asked for a TagErrorRenderer, it is supplied with the instance of InputTagSupport (the base class for all Stripes' input tags) that is in error. That gives you the information you need to provide different error renderer implementations (or just differently configured instances) based on the type of tag supplied, the tag's attributes, or information about the tag's containing tag and so on.

Let's assume for now that we stick with a single TagErrorRenderer, and that our goal is to ouput a red asterisk next to the field. The first thing we'll need to do is to implement a TagErrorRenderer.

A custom TagErrorRenderer
public class CustomTagErrorRenderer implements TagErrorRenderer {
    private InputTagSupport tag;
 
    /** Store the tag that is in error. */
    public void init(InputTagSupport tag) {
        this.tag = tag;
    }
 
    /** Output our asterisk before the tag/field itself is rendered. */
    public void doBeforeStartTag() {
        try {
            this.tag.getPageContext().getOut().write
                ("<span style=\"color:red; font-weight:bold;\">*</span>");
        }
        catch (IOException ioe) {
            // Not really a whole lot we can do if writing to out fails!
        }
    }
 
    /** Doesn't need to do anything. */
    public void doAfterEndTag() { }
}

Change and Change Back

If you modify the state of the tag in a TagErrorRenderer, it is important that at the end of doAfterEndTag() you take steps to restore the tag to it's original state. This is because many modern
Servlet/JSP containers pool custom tags, and they will expect the tag to be in the same state it was after the container finished invoking setter methods on it. If you changed anything, and forgot to change it back, this won't be true - and you might start seeing weird bugs in your JSPs.

Now that we have our own TagErrorRenderer we have to tell Stripes to use it. The easiest way to do this is to just have it automatically loaded by putting the class in a package that's configured with the Extension.Packages parameter (see Extensions). Alternatively, you can configure the DefaultTagErrorRendererFactory to use the new renderer by using this init-param:

<init-param>
    <param-name>TagErrorRenderer.Class</param-name>
    <param-value>com.myco.web.util.CustomTagErrorRenderer</param-value>
</init-param>

For more details on configuration see the Runtime Configuration section in the Configuration Reference

  • No labels
Configuration Reference - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

Stripes requires minimal configuration to get up and running, and zero configuration per ActionBean or page. That said, it is possible to change some of Stripes' behaviour using configuration. The way this is handled is that a Configuration object is responsible for supplying instances of various ConfigurableComponents to parts of Stripes that need them.

When you fire up Stripes without any additional Configuration, Stripes will use an instance of RuntimeConfiguration that extends the DefaultConfiguration and is capable of overriding the defaults if configured to do so. The following sections will walk through how to configure the components of Stripes that are used by default, and how to override those components with custom components developed for your project. Note that while you may supply alternative implementations for any ConfigurableComponent, not all the default implementations have configuration properties of their own.

Initial Configuration

To get up and running all you need to do is configure the Stripes dispatcher servlet, and the Stripes filter in your web.xml. The following shows an example configuration.

Configuring Stripes in your web.xml
<?xml version="1.0" encoding="UTF-8"?>
 
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
    version="2.4">
 
    <description>A description of your web application</description>
    <display-name>Your web application's name</display-name>
 
    <filter>
        <display-name>Stripes Filter</display-name>
        <filter-name>StripesFilter</filter-name>
        <filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class>
 
        <init-param>
            <param-name>ActionResolver.Packages</param-name>
            <param-value>my.action.bean.pkg</param-value>
        </init-param>
    </filter>
 
    <filter-mapping>
        <filter-name>StripesFilter</filter-name>
        <url-pattern>*.jsp</url-pattern>
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>
 
    <filter-mapping>
        <filter-name>StripesFilter</filter-name>
        <servlet-name>StripesDispatcher</servlet-name>
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>
 
    <servlet>
        <servlet-name>StripesDispatcher</servlet-name>
        <servlet-class>net.sourceforge.stripes.controller.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
 
    <servlet-mapping>
        <servlet-name>StripesDispatcher</servlet-name>
        <url-pattern>*.action</url-pattern>
    </servlet-mapping>
</web-app>

Note that the last piece, configuring a servlet mapping for *.action can be replaced or augmented with any pattern you choose.

The ActionResolver.Packages parameter

The ActionResolver.Packages Stripes Filter init-param is required. Indicate one or more package roots (without .* — subpackages are automatically included) where your ActionBean classes are. Stripes auto-discovers your ActionBeans at runtime by scanning those packages and their subpackages. This saves you from having to enumerate all your ActionBeans somewhere and maintaining that when you add, change, or remove ActionBeans.

Supplying Additional Configuration

In order to keep things simple, there are no external configuration files with Stripes. All of the ConfigurableComponents in Stripes use the same mechanism to find configuration properties. When a property is looked for, it is first looked for as an initialization parameter to the Stripes Filter. If a value is not found there, the property is looked for as an initialization parameter for the Web Application within which Stripes is running. If a value cannot be found there, it is looked for in the Java System Properties.

It is expected that in 99% of the cases all configuration will be done using initialization parameters for the Stripes Filter, since in most cases you will want to have a single Stripes application per web application. The other two levels become more useful if you have multiple Stripes applications and want to share configuration information across them, or want to be able to supply different values at the command line for different environments.

Configuration Configuration

As mentioned above, Stripes uses a Configuration object to drive it's runtime configuration. Two implemenations ship with Stripes. The DefaultConfiguration does not take any parameters itself, but initializes the default implementation for each component, and the components themselves may take parameters. The RuntimeConfiguration uses properties to determine at runtime which component implementations to instantiate. Despite the names, unless you configure it otherwise, Stripes will use a RuntimeConfiguration (DefaultConfiguration is named so because it provides the default configuration information, not because it is the default).

To specify a custom Configuration implementation use:

Property

Value

Default

Configuration.Class

The fully qualified name of a class that implements Configuration

net.sourceforge.stripes.config.RuntimeConfiguration

For example, to switch to using your own Configuration, you might make the following change to your web.xml:

Using a different configuration
<filter>
    <display-name>Stripes Filter</display-name>
    <filter-name>StripesFilter</filter-name>
    <filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class>
 
    <init-param>
        <param-name>Configuration.Class</param-name>
        <param-value>com.myco.CustomConfiguration</param-value>
    </init-param>
</filter>

RuntimeConfiguration Properties

The RuntimeConfiguration supports properties for specifying the implementation of each configured component. For more information on what each component is and what it does, click on the links for each of the interfaces. The supported properties are:

Property

Value

Default

ActionResolver.Class

The FQN of a class that implements ActionResolver

net.sourceforge.stripes.controller.NameBasedActionResolver

ActionBeanPropertyBinder.Class

The FQN of a class that implements ActionBeanPropertyBinder

net.sourceforge.stripes.controller.DefaultActionBeanPropertyBinder

ActionBeanContextFactory.Class

The FQN of a class that implements ActionBeanContextFactory

net.sourceforge.stripes.controller.DefaultActionBeanContextFactory

CoreInterceptor.Classes

Interceptors that are loaded by Stripes, regardless to Interceptor.Class param. Those are loaded before Interceptors' one, if any

net.sourceforge.stripes.controller.BeforeAfterMethodInterceptor, net.sourceforge.stripes.controller.HttpCacheInterceptor

ExceptionHandler.Class

The FQN of a class that implements ExceptionHandler

net.sourceforge.stripes.exception.DefaultExceptionHandler

Extension.Packages

A list of packages that will be searched for Stripes extensions. Examples of extensions include interceptors, formatters, type converters, exception handlers, object post-processors (release 1.6. and later) and custom implementations of ActionResolver, ActionBeanContext, ActionBeanContextFactory, FormatterFactory, LocalePicker, LocalizationBundleFactory, MultipartWrapperFactory, PopulationStrategy, TagErrorRendererFactory, TypeConverterFactory and ObjectFactory (release 1.6 and later). Most applications can simply set the Extension.Packages, ActionResolver.Packages and Stripes.EncryptionKey parameters and be done.

No default value. If this property is not set then extension autodiscovery will be disabled.

FormatterFactory.Class

The FQN of a class that implements FormatterFactory

net.sourceforge.stripes.format.DefaultFormatterFactory

Interceptor.Classes

The comma-separated list of fully qualified class names of Interceptor classes to use. The list may contain additional whitespace. Entries higher up the list will be invoked before entries lower down.

No default values. See CoreInterceptor for others Interceptors loaded by Stripes.

LocalePicker.Class

The FQN of a class that implements LocalePicker

net.sourceforge.stripes.localization.DefaultLocalePicker

LocalizationBundleFactory.Class

The FQN of a class that implements LocalizationBundleFactory

net.sourceforge.stripes.localization.DefaultLocalizationBundleFactory

MultipartWrapperFactory.Class

The FQN of a class that implements MultipartWrapperFactory

net.sourceforge.stripes.controller.multipart.DefaultMultipartWrapperFactory

PopulationStrategy.Class

The FQN of a class that implements PopulationStrategy

net.sourceforge.stripes.tag.DefaultPopulationStrategy

TagErrorRendererFactory.Class

The FQN of a class that implements TagErrorRendererFactory

net.sourceforge.stripes.tag.DefaultTagErrorRendererFactory

TypeConverterFactory.Class

The FQN of a class that implements TypeConverterFactory

net.sourceforge.stripes.validation.DefaultTypeConverterFactory

Stripes.DebugMode

A boolean flag that turns debug mode off or on. This should only be set to true during development. Currently, debug mode only turns off encryption, allowing a developer to read values that would otherwise be encrypted before being written to the client.
WARNING: Do not turn on debug mode in a production server. When encryption is disabled, you risk exposing resources that are normally protected, such as those under /WEB-INF.

false

Stripes.EncryptionKey

A (preferably very long, very random) string that is used as the encryption key when Stripes encrypts values before sending them to the client. If no encryption key is specified, then a random one is generated each time the application is initialized. When a random key is used, encrypted values do not survive across application reloads.

No default value. A random key is generated each time the application is initialized.

Stripes.HtmlMode

The type of HTML tags Stripes should generate. Options are html or xhtml

xhtml

VFS.Classes

The comma-separated list of fully qualified class names of VFS implementations to use in addition to the built-in implementations shipped with Stripes. The list may contain additional whitespace. The implementations are tried in the order they are specified and before any built-ins.

No default value.

ActionResolver Properties

The properties in this section affect how the AnnotatedClassActionResolver and NameBasedActionResolver (the only ActionResolvers included with Stripes) function. The ActionResolver is responsible for finding implementations of ActionBean at runtime. The included ActionResolvers do this by searching your web application classpath for all classes that implement ActionBean. While an exhaustive search is guaranteed to find everything, it can be quite slow. In reality your ActionBeans probably all get compiled and placed into a common package.

Property

Value

Default

ActionResolver.Packages

A comma-separated list of package roots where your ActionBean classes are. Stripes auto-discovers your ActionBeans at runtime by scanning those packages and their subpackages. This saves you from having to enumerate all your ActionBeans somewhere and maintaining that when you add, change, or remove ActionBeans.

No defaut value. This parameter is required.

ActionBeanContextFactory Properties

The DefaultActionBeanContextFactory allows you to provide an application specific subclass of ActionBeanContext to be used within your application. This is useful as it allows you to isolate yourself from any interaction with HttpServletRequest and HttpServletResponse in your ActionBeans by providing type safe getters and setters for any attributes you need to access in session etc.

Property

Value

Default

ActionBeanContext.Class

The FQN of a class which extends ActionBeanContext

net.sourceforge.stripes.action.ActionBeanContext

ExceptionHandler Properties

The DefaultExceptionHandler does not make use of any configuration properties. However, the DelegatingExceptionHandler also included in Stripes does. Like the ActionResolvers, it scans the classpath for classes (in this case AutoExceptionHandler classes), and can be configured to look in only specific places.

Property

Value

Default

DelegatingExceptionHandler.Packages

A comma separated list of package roots where your ExceptionHandlers are. Stripes will also search the packages specified in Extension.Packages for ExceptionHandlers so this parameter usually is not necessary.

Empty - search the packages specified by Extension.Packages.

LocalePicker Properties

The LocalePicker is responsible for picking the Locale under which a request will be processed, and optionally a character encoding. In non-localized systems this is usually just the system locale and character encoding, but in localized systems a match needs to be performed between the list of Locales the user accepts and the list of Locales the server can provide. Properties in this section affect how the DefaultLocalePicker (the only LocalePicker included with Stripes) functions.

Property

Value

Default

LocalePicker.Locales

A comma separated list of locales that the system supports. Each locale can be one to three segments long (e.g. en or en-us or en-us-mac). Either hypens or underscores can be used to separate the segments. In addition each locale can optinally include a character encoding to use with that locale. The character encoding is specified by appending :encoding to the locale, e.g. en_US:UTF-8

The system default locale as returned by Locale.getDefault().

LocalizationBundleFactory Properties

The LocalizationBundleFactory is responsible for providing the rest of Stripes somewhere to lookup localized resources. At present Stripes defines two types of resources - error messages and field names - that may be looked up from the same or different bundles. The properties in this section affect how the DefaultLocalizationBundleFactory (the only LocalizationBundleFactory included with Stripes) operates.

Property

Value

Default

LocalizationBundleFactory.ErrorMessageBundle

The name of a resource bundle. This may by any kind of resource bundle, but will typically be a property resource bundle, in which case the resource files should be available in the classpath.

StripesResources which will lead to a file called StripesResources.properties being looked for on the classpath, and potentially locale specific properties files as well.

LocalizationBundleFactory.FieldNameBundle

The name of a resource bundle. This may by any kind of resource bundle, but will typically be a property resource bundle, in which case the resource files should be available in the classpath.

StripesResources which will lead to a file called StripesResources.properties being looked for on the classpath, and potentially locale specific properties files as well.

File Upload / MultipartWrapperFactory Properties

Stripes provides an easy way to process file uploads using the HTTP multipart/form-data standard. This section documents properties that affect how the file upload process works.

Property

Value

Default

FileUpload.MaximumPostSize

The maximum HTTP Post size, in bytes, that will be accepted by the server. Note that this is not the maximum file size, but the combined size of all files in a single upload plus any other form fields and headers. In practical terms, this should be slightly larger (say 20-50k) than the maximum file size you want to handle, in order to allow for a reasonable amount of other data to come in the same request. The number is assumed to be in bytes unless a k/kb/m/mb/g/gb suffix is applied.

10 Megabytes.

MultipartWrapper.Class

The fully qualified name of the class, implementing MultipartWrapper.

net.sourceforge.stripes.controller.multipart.CommonsMultipartWrapper

Validation Properties

By default Stripes will not invoke the validation methods on ActionBeans if earlier validations (type conversion and annotation driven validation) resulted in validation errors. This behaviour can be overridden:

Property

Value

Default

Validation.InvokeValidateWhenErrorsExist

When true Stripes will call the validate() method regardless of whether earlier validation phases produced validation errors or not. (Introduced in Stripes 1.2)

false

 

  • No labels

1 Comment

  1. Hi,

    Could you please elaborate on multiple stripes applications in a war file.

    I am looking for something like as below:

    App 1 --> Customers management

    App 2 --> Order Management

    App 3 --> Product Management

    App 4 --> Shipping Management

    App 5 --> Authentication/Authorization

    Is it possible by using Stripes multiple apps mechanism to deploy all of the above apps individually in their own .war files; at the same time allow them communicate with each other without resorting to SOAP/REST or any other integration patterns. I mean, communicate exclusively within Stripes boundaries.

    For example, when an ORDER is received from a CUSTOMER for a PRODUCT that needs to be SHIPPED to an address. Apps/modules 1 to 4 will speak to 5 obviously, at the same time Order will check Customer module to check if the customer exists in db. Similarly,

    Is there anything possible similar to Django(Python) installable apps or Padrino(Ruby) mountable apps/sub apps in Stripes ?

    Any code examples are much appreciated.

    Many Thanks.

     

Stripes Around The Web - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

Stripes IDE plugins

Libraries Built With Stripes

  • Gripes - Stream-lined development with the Stripes Framework using Groovy and Gradle
  • Stripes Injection Enricher - Satisfy injection points specified declaratively using @EJB, @Inject and @Resource standard Java EE annotations
  • Stripes XSS Interceptor - Stripes XSS Sanitizer. Follows the XSS (Cross Site Scripting) security guidance posted at OWASP
  • Stars - Enables seamless integration with JPA, Spring, EJB and Stars service seamlessly via annotation configuration
  • Stripes Stuff - A collection of Stripes plugins, including Stripersist, the successor to Stripernate; a security plugin; a JavaScript library for client-side validation; and more
  • syracus-stripes - an extension to allow the seamless lookup of Resolution paths (generally views) from localized bundles
  • Woko - POJOs on the Web!  Full stack, Domain Driven framework.

Web Sites Built With Stripes

Web Application Build With Stripes

Published Book

Published Articles and Talks

Blog Entries

  • No labels
Tag Library Doc - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

This page contains high level information about the Stripes tag library. Other more detailed sources of information concerning the tag library include:

Usage

Stripes includes two Tag Library Descriptors inside the stripes.jar contained within the Stripes distribution. To use the Stripes tag library simply ensure that stripes.jar is located in your web applications /WEB-INF/lib directory, and include the following line at the top of your JSP file:

Standard Tag Library

<%@ taglib prefix="stripes" uri="http://stripes.sourceforge.net/stripes.tld" %>

DynAttr Tag Library

<%@ taglib prefix="stripes" uri="http://stripes.sourceforge.net/stripes-dynattr.tld" %>

Dynamic Attributes

The Stripes standard tag library does not permit the use of non-standard attributes for an tag that is the analog of an HTML tag, for example <stripes:text>. As a result they also do not allow dynamic attributes (dynamic attributes are a JSP system for accepting arbitrary attributes who's names are not know when the tag is written). The primary reason for this is to cut down on runtime errors for JSP developers. Most modern IDEs will catch mistakes in tag attributes and alert the developer. A fairly benign example might be:

<stripes:submit name="save" onlick="validate(this)"/>

With dynamic attributes disabled IDEs and JSP compilers will catch this error. With dynamic attributes enabled they will not, as it is valid (just not what the developer intended.

However with the advent of AJAX and JavaScript libraries it is sometimes desirable to use non-HTML attributes in HTML tags. For the reasons outlined above Stripes includes two slightly different TLDs that can be used. The standard one does not allow dynamic attributes in HTML tags and should be used in most cases. The second one allows dynamic attributes. It is possible (even recommended) to use both TLDs in one page. Doing so will allow the standard library to be used where possible, and the dynamic attribute one only where absolutely necessary. E.g.:

<%@ taglib prefix="s" uri="http://stripes.sourceforge.net/stripes.tld" %>
 
<s:form action="/my/NewsSearch.action">
    <s:text name="keywords"/>
    <d:text name="publicationDate" dojoType="calendar"/>
    <s:submit name="search"/>
</s:form>

Input Tag Population and Repopulation

The Stripes input tags (those that generate form form fields) are designed to be able to pre-populate and re-populate their own values based on values in the request, the ActionBean and values specified on the page. The order in which various sources are checked when populating a tag's value is determined by the configured PopulationStrategy. Unless otherwise configured Stripes uses an instance of DefaultPopulationStrategy for this purpose.

The DefaultPopulationStrategy searches in the following order for the first non-null value(s) when populating a given input tag:

  1. The HttpServletRequest parameter map for values matching the name of the input tag
  2. The ActionBean for a property or nested property matching the name of the input tag
  3. The value specified by the tag itself (varies by tag; usually as a value attribute, or as the body of the tag)

The reasoning behind this is as follows:

  • What the user entered should take precedence when re-displaying input to that same user
  • Values in the ActionBean usually represent domain values, and are common sources or pre-population
  • Values on the page are usually defaults specified for when no other applicable value is present

Stripes also includes a second population strategy called BeanFirstPopulationStrategy. The semantics of this population strategy are quite different - it's search strategy is:

  1. If the field in question has errors, revert to the DefaultPopulationStrategy
  2. Otherwise if an ActionBean is present and has a matching property, use it's value even if it is null
  3. Otherwise look for a non-null value specified on the page
  4. And lastly, look for a non-null value in the HttpServletRequest

If neither of these strategies meets your needs, worry not! It is very easy to change this behaviour - the DefaultPopulationStrategy was built with subclassing in mind. It provides individual methods to fetch an approprate value for the tag from the request, from the ActionBean and from the tag itself. It also contains a helper method to determine if the form is in error - in case it is desirable to modify behaviour based on error state. In order to change the PopulationStrategy all you need to do is subclass the DefaultPopulationStrategy and override the getValue(InputTagSupport tag) method to search in a different order. An example class is below:

An alternative population strategy
public class MyPopulationStrategy extends DefaultPopulationStrategy {
    /** Strategy to look at the ActionBean first, then the request, then the page.*/
    public Object getValue(InputTagSupport tag) throws StripesJspException {
        Object value = getValueFromActionBean(tag);
        if (value==null) value = getValuesFromRequest(tag);
        if (value==null) value = getValueFromTag(tag);
 
    return value;
    }
}

Configuring the alternative population strategy is as simple as adding an initialization parameter for the StripesFilter, e.g.

Configuring an alternative population strategy
....
<init-param>
    <param-name>PopulationStrategy.Class</param-name>
    <param-value>com.myco.web.MyPopulationStrategy</param-value>
</init-param>
....
  • No labels
File Uploads - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

Adding a File Upload to a Form

Stripes makes it very easy to manage file uploads. At its simplest you just include a file field in a stripes form, and match it with a FileBean property on your ActionBean. For example:

Using a file field on a JSP
<stripes:form>
    ...
    <stripes:file name="newAttachment"/>
    ...
</stripes:form>
A FileBean property in an ActionBean
private FileBean newAttachment;
 
public FileBean getNewAttachment() {
    return newAttachment;
}
 
public void setNewAttachment(FileBean newAttachment) {
    this.newAttachment = newAttachment;
}

At this point, it would be worth going and taking a look at the JavaDoc for FileBean.

Processing Uploaded Files

Unfortunately, we're not quite done yet. Due to limitations in the HTTP specification, the way file uploading is handled may not be quite as simple as you might at first think. It is worth taking the time to understand this. While the HTTP specification defines the multipart/form-data MIME type to allow the uploading of one or more files at the same time as other request data, it does not specify that the parts of the request must come in any specific order. This means that the browser can choose to send the files for upload before any of the other request parameters (read: information in your form).

What this means is that uploaded files cannot be streamed directly to your ActionBean, because you may not be able to access other request information until after the file is uploaded. To get around this Stripes, like other implementations, streams the files to a disk, processes the request and then provides you with access to the files on the local disk. This has repercussions for how you handle the FileBean objects that get attached to your ActionBean. Generally you're going to want save the uploaded file somewhere, or read it in as a stream and do something else with it...

Saving an uploaded file

Saving the uploaded file to a specific location can be achieved simply by calling the FileBean.save(File toFile) method. All this method does is move the temporary file saved during the upload processing to the location specified by the File you provide. If you invoke this method, since the temporary file gets moved, there is no need (in fact there is a need not) to call FileBean.delete().

Reading the uploaded file as a Stream

The alternative is to process the uploaded file as an InputStream. You might choose to do this if you want to stream the contents of the file to a database, or analyze the data that is uploaded. You can do this by calling FileBean.getInputStream() and then reading the data as you would with any other InputStream. Once you are done you should call FileBean.delete() to delete the temporary file. If you don't do this then your web application's temporary directory will fill up with all the uploaded files!

Know when to delete FileBeans

If you process a FileBean as an InputStream, call FileBean.delete(). If you use the save() method, don't call FileBean.delete()!

Multiple File Uploads Using Indexed Properties

Sometimes it will be necessary to support the uploading of more than one file where you (the developer) doesn't know the number ahead of time. For non-file fields you might do this just by including multiple copies of the field all with the same name and binding that to a List or Array in your ActionBean. Stripes doesn't support this with FileBeans though due to a limitation in one of the Multipart libraries support. It is, however, easy to mimic this behaviour using Stripes' indexed property support which works the same for FileBeans as it does for other inputs.

The following code shows how you might refactor the code from above to accept multiple files:

Using an indexed file field on a JSP
<stripes:form>
    <c:forEach ... varStatus="loop">
    ...
    <stripes:file name="newAttachments[${loop.index}]"/>
    ...
</stripes:form>
An Indexed FileBean property in an ActionBean
private List<FileBean> newAttachments;
 
public List<FileBean> getNewAttachments() {
    return this.newAttachments;
}
 
public void setNewAttachment(List<FileBean> newAttachments) {
    this.newAttachments = newAttachments;
}

Limiting the size of uploads

Unfortunately the HTTP specification gets in the way again here. The only piece of useful information available before the contents of the POST is the total size of the POST. This includes all uploaded files, all other fields in the form, any headers etc. As a result, the only thing that we can use to limit uploads is the POST size (duh!). You'll want to consider a limit carefully - it should be low enough to prevent denial of service attack, but high enough to let your users upload the kinds of files you want them to.

By default Stripes set this limit to 10 megabytes. For information on how to change the limit refer to the File Upload section in the Configuration Reference.

Alternative Implementations

The parsing of multipart form data is quite difficult to do correctly, and for this reason Stripes delegates this responsibility to well established third party libraries. The code which performs the parsing is wrapped behind an interface called MultipartWrapper. Stripes provides two implementations of MultipartWrapper.

Why two? Earlier version of Stripes shipped with a single (non-pluggable) implementation that used the COS package. This was done for two major reasons: it has no dependencies (i.e. no other jars that must be used with it) and it provided a more intuitive programming model (for me, developing Stripes). However, the licensing for COS is less than ideal. While it will suit most commercial developers it excludes the possibility of redistributing your application without negotiating a commercial license for COS!

The result is that the second implementation now uses the Apache Commons File Upload which is covered under the much more permissive Apache License (the same license used by Stripes). The only real downside is that the commons implementation has a dependency on the commons-io package.

For backwards compatibility reasons the COS implementation has been refactored into an implementation of MultipartWrapper called (surprisingly) CosMultipartWrapper, which continues to be the default implementation.

In case you're going to use CommonsFileupload and also have the cos library in your classpath, you can explicitly configure the CommonsMultipartWrapper by adding the following initialization parameter to the Stripes Filter:

<init-param>
    <param-name>MultipartWrapper.Class</param-name>
    <param-value>net.sourceforge.stripes.controller.multipart.CommonsMultipartWrapper</param-value>
</init-param>

It is also possible to plug-in replace the code that is responsible for manufacturing an instance of MultipartWrapper in order to add additional control behaviour. This can be done by implementing MultipartWrapperFactory and specifying your custom factory as an initialization parameter to the Stripes Filter.

<init-param>
    <param-name>MultipartWrapperFactory.Class</param-name>
    <param-value>com.myco.CustomMultipartWrapperFactory</param-value>
</init-param>

 

  • No labels
AJAX - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

AJAX is something that you must have been living under a rock if you haven't heard of before now, so I'm not going to do the "this is what AJAX is" routine. If you need that, I'd suggest you go here. Since AJAX is very much a client-side technology, and Stripes is a server side framework, this document will mainly focus on how to interact with Stripes using AJAX technologies. If you're looking for neat visual effects and general AJAX tips, you'd be better off searching google!

There are many different ways to write AJAX applications. At the simplest level you can imagine invoking some logic on the server (or even just fetching static content) and swapping the contents of visible elements on the screen without refreshing the page. More complex (and hence powerful) approaches involve passing back structured data as XML, or JSON constructs, which can then be manipulated in sophisticated ways on the client using JavaScript.

This "How To" will walk through:

  • How to invoke Stripes ActionBeans using AJAX
  • An AJAX version of the Calculator application from the Quick Start Guide
  • Some additional techniques for using AJAX with Stripes

Invoking ActionBeans using AJAX

Probably the first thing you'll need to do is invoke an ActionBean from the browser using JavaScript. There are a large number of AJAX frameworks out there and almost all of them contain helper methods and/or objects to accomplish this. Stripes does not require any specific AJAX framework, and will work equally well whether you choose Dojo, MochiKit, Prototype or any of the many other client side toolkits.

For the purpose of these examples we'll be using Prototype. Prototype is an excellent library that does a good job of abstracting away browser differences and adds a sane baseline over the often quirky JavaScript APIs.

AJAX Calculator Application

Although the calculator application is somewhat trivial, it serves as a good example for how to use Stripes with AJAX. The following is the JSP from the Quick Start Guide modified a little (if you haven't read the quick start, you might want to at least take a look at the example code for comparison):

/ajax/index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="stripes" uri="http://stripes.sourceforge.net/stripes.tld"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>My First Ajax Stripe</title>
<script type="text/javascript"
src="${pageContext.request.contextPath}/ajax/prototype.js"></script>
<script type="text/javascript" xml:space="preserve">
    /*
    * Function that uses Prototype to invoke an action of a form. Slurps the values
    * from the form using prototype's 'Form.serialize()' method, and then submits
    * them to the server using prototype's 'Ajax.Updater' which transmits the request
    * and then renders the response text into the named container.
    *
    * NOTE: Requires Prototype version 1.6 or above.
    *
    * @param form reference to the form object being submitted
    * @param event the name of the event to be triggered, or null
    * @param container the name of the HTML container to insert the result into
    */
    function invoke(form, event, container) {
        if (!form.onsubmit) { form.onsubmit = function() { return false } };
        var params = Form.serialize(form, {submit:event});
        new Ajax.Updater(container, form.action, {method:'post', parameters:params});
    }
</script>
</head>
<body>
<h1>Stripes Ajax Calculator</h1>
 
<p>Hi, I'm the Stripes Calculator. I can only do addition. Maybe, some day, a nice programmer
will come along and teach me how to do other things?</p>
 
<stripes:form action="/examples/ajax/Calculator.action">
    <table>
        <tr>
            <td>Number 1:</td>
            <td><stripes:text name="numberOne"/></td>
        </tr>
        <tr>
            <td>Number 2:</td>
            <td><stripes:text name="numberTwo"/></td>
        </tr>
        <tr>
            <td colspan="2">
                <stripes:submit name="add" value="Add"
                    onclick="invoke(this.form, this.name, 'result');"/>
                <stripes:submit name="divide" value="Divide"
                    onclick="invoke(this.form, this.name, 'result');"/>
            </td>
        </tr>
        <tr>
            <td>Result:</td>
            <td id="result"></td>
        </tr>
    </table>
</stripes:form>
</body>
</html>

There are a few changes to note. Working from the bottom up, we see:

<td>Result:</td>
<td id="result"></td>

In this case, we no longer insert the result from the ActionBean using EL, because the result isn't rendered into the JSP on the server side. Instead the place where the result will go is identified by an id so that we can reference it after the page has loaded. Next we see:

<stripes:submit name="add" value="Add"
    onclick="invoke(this.form, this.name, 'result');"/>
<stripes:submit name="divide" value="Divide"
    onclick="invoke(this.form, this.name, 'result');"/>

Here we see submit buttons that trigger a javascript function when they are clicked, and pass it the form, the name of the button that was clicked (as the Stripes event name) and the name of the HTML element into which to insert the result. Lastly we see:

<script
    type="text/javascript"
    src="${pageContext.request.contextPath}/ajax/prototype.js"></script>
<script type="text/javascript" xml:space="preserve">
    /* ... */
    function invoke(form, event, container) {
        if (!form.onsubmit) { form.onsubmit = function() { return false } };
        var params = Form.serialize(form, {submit:event});
        new Ajax.Updater(container, form.action, {method:'post', parameters:params});
    }
</script>

The first script tag imports the Prototype JavaScript library. The second block defines a little utility function that uses Prototype to invoke an ActionBean on the server and update the contents of an HTML container when the server delivers the result. Before making the call to the server, it disables the normal form submission process by setting up the form's "onsubmit" event handler to return false.

That's it for the JSP. Assuming we have everything setup for the ActionBean to work right, the page will contact the server when a button is clicked and render the response into the page without refreshing the browser window.

Let's take a look at the ActionBean:

"AJAX CalculatorActionBean.java"
package net.sourceforge.stripes.examples.ajax;
 
import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.action.StreamingResolution;
import net.sourceforge.stripes.validation.Validate;
import net.sourceforge.stripes.validation.ValidationError;
import net.sourceforge.stripes.validation.ValidationErrorHandler;
import net.sourceforge.stripes.validation.ValidationErrors;
 
import java.io.StringReader;
import java.util.List;
 
/**
* A very simple calculator action that is designed to work with an ajax front end.
* @author Tim Fennell
*/
public class CalculatorActionBean implements ActionBean, ValidationErrorHandler {
    private ActionBeanContext context;
    @Validate(required=true) private double numberOne;
    @Validate(required=true) private double numberTwo;
 
    public ActionBeanContext getContext() { return context; }
    public void setContext(ActionBeanContext context) { this.context = context; }
 
    @DefaultHandler public Resolution add() {
        String result = String.valueOf(numberOne + numberTwo);
        return new StreamingResolution("text", new StringReader(result));
    }
 
    public Resolution divide() {
        String result = String.valueOf(numberOne / numberTwo);
        return new StreamingResolution("text", new StringReader(result));
    }
 
    // Standard getter and setter methods
    public double getNumberOne() { return numberOne; }
    public void setNumberOne(double numberOne) { this.numberOne = numberOne; }
 
    public double getNumberTwo() { return numberTwo; }
    public void setNumberTwo(double numberTwo) { this.numberTwo = numberTwo; }
}

This looks very similar to the code from the quickstart example, other than the implementations of add and divide. Instead of setting an attribute on the ActionBean and forwarding the user to a JSP, the handler methods now perform the calculation and then simply sends the result back to the client as text using a StreamingResolution:

@DefaultHandler
public Resolution add() {
    String result = String.valueOf(numberOne + numberTwo);
    return new StreamingResolution("text", new StringReader(result));
}

That's it for the basics. Our page will now contact our ActionBean when one of the buttons is hit, and the result will get transferred back to the page and displayed in the appropriate place.

Handling Validation Errors

This all works great, until some validation errors occur. Then Stripes tries to forward the user back to the page that originated the request and render it with errors. With the code shown above, this results in the page getting embedded in itself! Oops. Luckily there is a way to handle this. Stripes provides an optional interface for ActionBeans called ValidationErrorHandler. This allows ActionBeans to intercept the flow of execution when validation errors occur, and tell Stripes what to do next.

At the top of our ActionBean we can add:

public class CalculatorActionBean implements ActionBean, ValidationErrorHandler {
    ...
    /** Converts errors to HTML and streams them back to the browser. */
    public Resolution handleValidationErrors(ValidationErrors errors) throws Exception {
        StringBuilder message = new StringBuilder();
 
        for (List<ValidationError> fieldErrors : errors.values()) {
            for (ValidationError error : fieldErrors) {
                message.append("<div class=\"error\">");
                message.append(error.getMessage(getContext().getLocale()));
                message.append("</div>");
            }
        }
 
        return new StreamingResolution("text/html", new StringReader(message.toString()));
    }
    ...
}

Because the ActionBean implements ValidationErrorHandler Stripes will invoke the handleValidationErrors method if any validation errors are generated. In this case the method loops through the set of errors constructing an HTML fragment with each error in a separate div. This is then returned to the client, again using the StreamingResolution. Now when validation errors occur they are displayed in the same place that the result would be displayed.

More Efficient Real-Time Streaming

The StreamingResolution works well when:

  • you are streaming data back to the client from a stream or reader object (e.g. streaming back a chunk of XML received from a web service invocation or the database)
  • you are generating a few kilobytes or less of information, programmatically in your ActionBean

If, however, you are generating a lot of output from your ActionBean and do not want to buffer it entirely to a String before starting to send it back to the client, one good way to approach the problem is to create an anonymous Resolution. Take a look at the following example:

Using an anonymous Resolution
@HandlesEvent("GetLotsOfData")
public Resolution getLotsOfData() {
    Map<String,String> items = getReallyBigMap();
    return new Resolution() {
        public void execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
            response.setContentType("text/xml");
 
            response.getOutputStream().print("<entries>");
            for (Map.Entry<String,String> entry : items.entries()) {
                response.getOutputStream().print("<entry><key>");
                response.getOutputStream().print(entry.getKey());
                response.getOutputStream().print("</key><value>");
                response.getOutputStream().print(entry.getValue());
                response.getOutputStream().print("</value></entry>");
            }
            response.getOutputStream().print("</entries>");
        }
    }
}

Using ForwardResolution to return html fragments

If your ajax request expects an html fragment as a response, you can use a ForwardResolution to generate it. The benefit to this is that the html is created by a jsp.

  1. Ajax.Updater makes a request
  2. Your ActionBean event handler forwards to a jsp
  3. Ajax.Updater updates $(container) with whatever html fragment the jsp renders

For example, if we ever decide to add a button to our calculator to reveal a hidden drawer containing additional functionality, we could create an event handler 'showScientificCalc()' and add a new jsp 'scientificCalcControls.jsp'.

public Resolution showScientificCalc() {
    return new ForwardResolution("/fictitious/scientificCalcControls.jsp");
}

Returning more complex data to the browser

In some situations you may want to return structured data to the browser instead of HTML for display. In general there are two ways to do this:

  • return an XML island containing your data
  • return JSON or JavaScript to be evaluated by the browser

While both methods work, my preference is for returning JavaScript because it's easier to work with in the browser and can support more complex data (e.g. cyclical object graphs). Stripes has support for this kind of interaction through the JavaScriptResolution and JavaScriptBuilder classes.

The JavaScriptBuilder is a class that can take a Java object of any type and traverse it to create a set of JavaScript statements and JSON blocks that will, when evaluated, recreate the object's state in JavaScript. It can handle all the Java built in types, and can traverse arbitrary user types. It even correctly handles circular object graphs, ensuring that each object is serialized only once and that all object references are valid.

The JavaScriptResolution is a Resolution that uses the builder to serialize an item to JavaScript and then stream it back to the client. Although it's unlikely that we'd use this technique for returning a single number, a modified set of JavaScript for the caclulator example using Prototype might look like this:

Handling a JavaScriptResolution
/** Function that handles the update when the async request returns. */
function update(xhr) {
    var output = eval(xhr.responseText);
    $('result').innerHtml = output;
}
 
/** Function to submit a request and invoke a handler when it completes. */
function invoke(form, event, handler) {
    var params = Form.serialize(form, {submit:event});
    new Ajax.Request(form.action, {method:'post', parameters:params, onSuccess:handler}});
}

 

  • No labels
1.5.x Maven Build and Continuous Integration - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

This page describes the maven build that has been introduced recently in the 1.5.x branch, and the continuous integration setup in our Jenkins instance.

Maven Build

We use maven to build Stripes artifacts and perform webtests. Our C.I. server fires maven builds.

We use the facilities provided by Sonatype for our maven repository : https://oss.sonatype.org/index.html

This allows us to deploy easily and sync with the maven central repository.

The maven build has the following modules structure :

  • stripes-parent (pom)
    • stripes (jar)
    • tests
    • examples (war)
    • webtests (war, optional, activated by profile)

As we wanted to keep the ant build untouched, we had to adapt to the files and directories layout of this one. Thereby, the default source/resource folders settings are often overriden in each pom.

stripes-parent

The project's root pom. We inherit from sonatype's pom in order to automate our deployment to oss.sonatype.org and sync with maven central.
This module serves mainly to centralize dependencies, and to host the submodules.

stripes

The Stripes module : produces the Stripes jar. It's a regular jar packaging module, with a few tricks to generate .tld files and the javadocs/tlddocs.

The module doesn't contain any tests, because of the file layout we have. We had to create a separate module to host the tests (see below)

tests

Module containing the Stripes unit tests. Doesn't produce any useful artifact, but fails the whole build if some tests don't pass.


IMPORTANT: As tests are built in a separate submodule, the main stripes.jar could be installed in the local repository even if there are test failures. This is due to maven installing each module sequentially inside a multimodule project even if one fails later in the build (installation isn't atomic for a multi module build). We'll probably refactor this later to put the tests inside the stripes module, and remove this test module.

examples

The stripes-examples war project. Produces the war artifact with calculator, bugzooky etc.

webtests

An optional module included via the profile 'webtests'. Contains the canoo web tests for the example app, as well as the maven plumbing to download/start tomcat and run the tests against the webapp. You can run the webtests only (without rebuilding everything) against the latest examples war like this :

It basically starts tomcat (via cargo plugin) for the integration tests phase of the maven lifecycle, and deploys the example app. Canoo webtests are then fired against the app. A nice report is generated in webtests/target/test-classes/webtest-results/.

There's a trick to notice here. I couldn't find a way to configure cargo to deploy the examples app, so I made the webtests module a war packaging module without anything in it. It thereby overlays with the examples app (in dependencies), and produces a war that is used by cargo for deployment. This could be improved...

Useful commands

Here are a few useful commands :
1.5.x> mvn clean install : compiles, tests and installs all stripes and examples in your local repo
1.5.x> mvn clean install -Dmaven.test.skip : same than above, skipping the tests
1.5.x> mvn clean install -Pwebtests : compiles, tests, webtests and installs stripes and examples in your local repo

You can build submodules individually of course ;
1.5.x/stripes> mvn clean install : compiles and installs stripes in your local repo
1.5.x/tests> mvn clean install : compiles and runs the tests on your latest installed stripes
1.5.x/webtests> mvn clean install : runs the webtests on your latest instaled examples app

Continuous Integration

We use Jenkins in order to build Stripes continuously: http://www.stripesframework.org/jenkins/

At each commit, we produce the Stripes snapshot jar. There are currently two jobs for the 1.5.x branch.

Stripes 1.5.x branch

Builds Stripes jar (compiles, runs unit tests, and builds stripes.jar) and deploys to oss.sonatype.org. Runs whenever code is commited.

Stripes 1.5.x webtests

Executes the Stripes webtests. Runs whenever Stripes is built.

  • No labels
FreeMarker with Stripes - Stripes Wiki - Stripes Framework Wiki
Skip to end of metadata
Go to start of metadata

This document is intended primarily to help those already familiar with FreeMarker integrate Stripes and FreeMarker for development of an application. It is not intended as an introduction to FreeMarker - FreeMarker has excellent documentation of it's own.

Setting up FreeMarker

The first step is to download FreeMarker. It is strongly recommended to use version 2.3.8 or above of FreeMarker. FreeMarker 2.3.8 includes several additions to the JSP tag support in order to support the full range of JSP 2.0 tag library APIs. Since some Stripes tags depend on JSP 2.0 APIs, they will not work with earlier version of FreeMarker.

Once FreeMarker is downloaded expand the distribution and copy lib/freemarker.jar into your web application classpath, probably at WEB-INF/lib/freemarker.jar.

Next you will need to configure the FreeMarker servlet. The following is a minimal configuration that can be added to your web.xml. You may wish to consult FreeMarker's documentation on using FreeMarker with servlets for more details and configuration options.

web.xml excerpt for FreeMarker servlet
<servlet>
    <servlet-name>Freemarker</servlet-name>
    <servlet-class>freemarker.ext.servlet.FreemarkerServlet</servlet-class>
 
    <init-param>
        <param-name>TemplatePath</param-name>
        <param-value>/</param-value>
    </init-param>
    <init-param>
        <param-name>template_update_delay</param-name>
        <param-value>0</param-value> <!-- 0 is for dev only! Use higher value otherwise. -->
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
 
<servlet-mapping>
    <servlet-name>Freemarker</servlet-name>
    <url-pattern>*.ftl</url-pattern>
</servlet-mapping>

Of course it is entirely possible to map the FreeMarker servlet to other extensions or url patterns, but for now we'll use *.ftl as the mapping. Similarly the TemplatePath specified tells FreeMarker that it should find the template files by looking in the web application file structure, starting at the root.

The last step in configuring FreeMarker is to ensure that the Stripes Filter filters requests that are made directly to FreeMarker templates. This step is optional, but highly recommended. If you never (and I mean never) allow navigation directly to FreeMarker templates (i.e. navigation is always through ActionBeans first and you always forward, never redirect to views) then this is not necessary.

web.xml excerpt for Filtering FreeMarker requests
<filter-mapping>
    <filter-name>StripesFilter</filter-name>
    <servlet-name>Freemarker</servlet-name>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

Using FreeMarker and Stripes

Once FreeMarker is installed and configured you can begin using it right away. The main part of Stripes that interacts heavily with the view tier is the tag library. Stripes does not provide an alternate macro library for use with FreeMarker, but thanks to FreeMarker's embedded lightweight JSP tag container, you can use the Stripes tag library with FreeMarker. To do this you'll need to add a single assign statement at the top of your FreeMarker templates:

Using Stripes tag library in a FreeMarker template

Since Stripes exposes the ActionBean as a request attribute, and FreeMarker makes all request attributes available in the template model, you can also reference ActionBean properties using FreeMarker's built in expression language - just like any other model object. Stripes does not use it's own expression language (when used with JSPs Stripes uses the JSP EL), so access to ActionBeans behaves exactly as you would expect in FreeMarker.

The Quickstart Example in FreeMarker

The following is a reworking of the Quick Start Guide's JSP into a FreeMarker template. The ActionBean itself is identical except for the replacement of "index.jsp" with "index.ftl".

FreeMarker version of the quickstart template - index.ftl
[#ftl]
<html>
<head>
<title>My First Stripe</title>
<style type="text/css">
input.error { background-color: yellow; }
</style>
</head>
<body>
<h1>Stripes Calculator - FTL</h1>
 
Hi, I'm the Stripes Calculator. I can only do addition. Maybe, some day, a nice programmer
will come along and teach me how to do other things?
 
[@s.form action="/examples/quickstart/Calculator.action"]
[@s.errors/]
<table>
    <tr>
        <td>Number 1:</td>
        <td>[@s.text name="numberOne"/]</td>
    </tr>
    <tr>
        <td>Number 2:</td>
        <td>[@s.text name="numberTwo"/]</td>
    </tr>
    <tr>
        <td colspan="2">
            [@s.submit name="addition" value="Add"/]
            [@s.submit name="division" value="Divide"/]
        </td>
    </tr>
    <tr>
        <td>Result:</td>
        <td>${(actionBean.result)!}</td>
    </tr>
</table>
[/@]
</body>
</html>

The first and most obvious change is that instead of using JSP syntax, the template uses FreeMarker's (newer) square bracket syntax for tags. The next change to note is the removal of the JSP taglig imports, and their replacement with the FreeMarker assign statement on line 2.

Stripes tags in JSP took the form of <stripes:tagname attr="value"/>. As you can see here, this has effectively been search-and-replaced to the form [@s.tagname attr="value". The change of prefix from 'stripes' to 's' is arbitrary (both prefixes are valid for both template systems) but the 's' feels more in keeping with FreeMarker's preference for terseness.

The last significant change is the following line:

<td>${(actionBean.result)!}</td>

The expression is wrapped in parenthesis and has the '!' applied to it. This is done because the first time the template renders the actionBean is null, and FreeMarker would otherwise through an exception. The '!' operator informs FreeMarker to ignore null values in the preceeding expression.

Gotchas

The following is a short list of problems that I, as a user familiar with JSP and unfamiliar with Freemarker, encountered while converting a small application from JSP to FreeMarker. These are not problems with FreeMarker per se, but for anyone familiar with JSP they are traps which are easy to fall in to.

1. Some Stripes tags have hyphens in their names. FreeMarker does not support this unless the tag names are quoted. So for example, to use the layout-render tag you have to write [@s["layout-render"]]. To close this tag you must use the shorthand [/@].

2. FreeMarker is less forgiving of null values than JSP is. Therefore for any bean value you access through an expression (e.g. actionBean.user.name) if the value may be null you will need to protect against this using one of FreeMarker's built in operators. E.g. actionBean.user.name! would render as the empty String if null, and actionBean.user.name!'New User' would render as 'New User'.

3. In FreeMarker it is a) not necessary and b) incorrect to quote non-String values. For example, in JSP we might write <stripes:text name="foo" value="${actionBean.user.dateOfBirth}"/> and the value supplied to the Stripes tag would the be the exact value of the property - probably a java.util.Date. In FreeMarker writing [@s.text name="foo" value="${actionBean.user.dateOfBirth}"/] would result in the String value of the property being supplied to the tag! The correct syntax in FreeMarker is more concise: [@s.text name="foo" value=actionBean.user.dateOfBirth/].

  • No labels